async def get_object_types() -> UpdatedObjectTypes: """Serves to initialize or update knowledge about awailable ObjectTypes. :return: """ initialization = False # initialize with built-in types, this has to be done just once if not glob.OBJECT_TYPES: logger.debug("Initialization of ObjectTypes.") initialization = True await hlp.run_in_executor(prepare_object_types_dir, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE) glob.OBJECT_TYPES.update(built_in_types_data()) updated_object_types: ObjectTypeDict = {} object_type_ids: Union[set[str], list[str]] = await storage.get_object_type_ids() if __debug__: # this should uncover potential problems with order in which ObjectTypes are processed import random object_type_ids = list(object_type_ids) random.shuffle(object_type_ids) for obj_id in object_type_ids: await get_object_data(updated_object_types, obj_id) removed_object_ids = { obj for obj in glob.OBJECT_TYPES.keys() if obj not in object_type_ids } - built_in_types_names() updated_object_ids = { k for k in updated_object_types.keys() if k in glob.OBJECT_TYPES } new_object_ids = { k for k in updated_object_types.keys() if k not in glob.OBJECT_TYPES } logger.debug(f"Removed ids: {removed_object_ids}") logger.debug(f"Updated ids: {updated_object_ids}") logger.debug(f"New ids: {new_object_ids}") if not initialization and removed_object_ids: # TODO remove it from sys.modules remove_evt = ChangedObjectTypes([ v.meta for k, v in glob.OBJECT_TYPES.items() if k in removed_object_ids ]) remove_evt.change_type = Event.Type.REMOVE asyncio.ensure_future(notif.broadcast_event(remove_evt)) for removed in removed_object_ids: assert removed not in built_in_types_names( ), "Attempt to remove built-in type." del glob.OBJECT_TYPES[removed] await hlp.run_in_executor(remove_object_type, removed) glob.OBJECT_TYPES.update(updated_object_types) logger.debug(f"All known ids: {glob.OBJECT_TYPES.keys()}") for obj_type in updated_object_types.values(): # if description is missing, try to get it from ancestor(s) if not obj_type.meta.description: try: obj_type.meta.description = obj_description_from_base( glob.OBJECT_TYPES, obj_type.meta) except otu.DataError as e: logger.error( f"Failed to get info from base for {obj_type}, error: '{e}'." ) if not obj_type.meta.disabled and not obj_type.meta.built_in: add_ancestor_actions(obj_type.meta.type, glob.OBJECT_TYPES) if not initialization: if updated_object_ids: update_evt = ChangedObjectTypes([ v.meta for k, v in glob.OBJECT_TYPES.items() if k in updated_object_ids ]) update_evt.change_type = Event.Type.UPDATE asyncio.ensure_future(notif.broadcast_event(update_evt)) if new_object_ids: add_evt = ChangedObjectTypes([ v.meta for k, v in glob.OBJECT_TYPES.items() if k in new_object_ids ]) add_evt.change_type = Event.Type.ADD asyncio.ensure_future(notif.broadcast_event(add_evt)) for obj_type in updated_object_types.values(): if obj_type.type_def and issubclass( obj_type.type_def, Robot) and not obj_type.type_def.abstract(): await get_robot_meta(obj_type) # if object does not change but its base has changed, it has to be reloaded for obj_id, obj in glob.OBJECT_TYPES.items(): if obj_id in updated_object_ids: continue if obj.type_def and obj.meta.base in updated_object_ids: logger.debug( f"Re-importing {obj.meta.type} because its base {obj.meta.base} type has changed." ) obj.type_def = await hlp.run_in_executor( hlp.import_type_def, obj.meta.type, Generic, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) return UpdatedObjectTypes(new_object_ids, updated_object_ids, removed_object_ids)
async def new_object_type_cb(req: srpc.o.NewObjectType.Request, ui: WsClient) -> None: async with ctx_write_lock(glob.LOCK.SpecialValues.ADDING_OBJECT, ui): meta = req.args if meta.type in glob.OBJECT_TYPES: raise Arcor2Exception("Object type already exists.") hlp.is_valid_type(meta.type) if meta.base not in glob.OBJECT_TYPES: raise Arcor2Exception( f"Unknown base object type '{meta.base}', " f"known types are: {', '.join(glob.OBJECT_TYPES.keys())}.") base = glob.OBJECT_TYPES[meta.base] if base.meta.disabled: raise Arcor2Exception("Base object is disabled.") assert base.type_def is not None if issubclass(base.type_def, Robot): raise Arcor2Exception("Can't subclass Robot.") meta.has_pose = issubclass(base.type_def, GenericWithPose) if not meta.has_pose and meta.object_model: raise Arcor2Exception( "Object without pose can't have collision model.") if req.dry_run: return None obj = meta.to_object_type() ast = new_object_type(glob.OBJECT_TYPES[meta.base].meta, meta) obj.source = tree_to_str(ast) if meta.object_model: if meta.object_model.type == Model3dType.MESH: # TODO check whether mesh id exists - if so, then use existing mesh, if not, upload a new one # ...get whole mesh (focus_points) based on mesh id assert meta.object_model.mesh try: meta.object_model.mesh = await storage.get_mesh( meta.object_model.mesh.id) except storage.ProjectServiceException as e: glob.logger.error(e) raise Arcor2Exception( f"Mesh ID {meta.object_model.mesh.id} does not exist.") else: meta.object_model.model().id = meta.type await storage.put_model(meta.object_model.model()) type_def = await hlp.run_in_executor( hlp.save_and_import_type_def, obj.source, obj.id, base.type_def, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) assert issubclass(type_def, base.type_def) actions = object_actions(type_def, ast) await storage.update_object_type(obj) glob.OBJECT_TYPES[meta.type] = ObjectTypeData(meta, type_def, actions, ast) add_ancestor_actions(meta.type, glob.OBJECT_TYPES) evt = sevts.o.ChangedObjectTypes([meta]) evt.change_type = events.Event.Type.ADD asyncio.ensure_future(notif.broadcast_event(evt)) return None
async def new_object_type_cb(req: srpc.o.NewObjectType.Request, ui: WsClient) -> None: async with ctx_write_lock(glob.LOCK.SpecialValues.ADDING_OBJECT, glob.USERS.user_name(ui)): meta = req.args if meta.type in glob.OBJECT_TYPES: raise Arcor2Exception("Object type already exists.") hlp.is_valid_type(meta.type) if meta.base not in glob.OBJECT_TYPES: raise Arcor2Exception( f"Unknown base object type '{meta.base}', " f"known types are: {', '.join(glob.OBJECT_TYPES.keys())}.") base = glob.OBJECT_TYPES[meta.base] if base.meta.disabled: raise Arcor2Exception("Base object is disabled.") assert base.type_def is not None if issubclass(base.type_def, Robot): raise Arcor2Exception("Can't subclass Robot.") meta.has_pose = issubclass(base.type_def, GenericWithPose) if issubclass(base.type_def, CollisionObject): if not meta.object_model: raise Arcor2Exception( "Objects based on CollisionObject must have collision model." ) else: if meta.object_model: raise Arcor2Exception( "Only objects based on CollisionObject can have collision model." ) if req.dry_run: return None obj = meta.to_object_type() ast = new_object_type(glob.OBJECT_TYPES[meta.base].meta, meta) obj.source = tree_to_str(ast) if meta.object_model: await update_object_model(meta, meta.object_model) type_def = await hlp.run_in_executor( hlp.save_and_import_type_def, obj.source, obj.id, base.type_def, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) assert issubclass(type_def, base.type_def) actions = object_actions(type_def, ast) meta.modified = await storage.update_object_type(obj) glob.OBJECT_TYPES[meta.type] = ObjectTypeData(meta, type_def, actions, ast) add_ancestor_actions(meta.type, glob.OBJECT_TYPES) evt = sevts.o.ChangedObjectTypes([meta]) evt.change_type = events.Event.Type.ADD asyncio.ensure_future(notif.broadcast_event(evt)) return None