async def managed_scene( scene_id: str, make_copy: bool = False ) -> AsyncGenerator[UpdateableCachedScene, None]: save_back = False if glob.SCENE and glob.SCENE.id == scene_id: if make_copy: scene = copy.deepcopy(glob.SCENE) save_back = True else: scene = glob.SCENE else: save_back = True scene = UpdateableCachedScene(await storage.get_scene(scene_id)) if make_copy: scene.id = common.Scene.uid() try: yield scene finally: if save_back: asyncio.ensure_future(storage.update_scene(scene.scene))
async def add_object_to_scene(scene: UpdateableCachedScene, obj: SceneObject, dry_run: bool = False) -> None: """ :param obj: :param add_to_scene: Set to false to only create object instance and add its collision model (if any). :return: """ check_object(scene, obj, new_one=True) if dry_run: return None scene.upsert_object(obj) glob.logger.debug(f"Object {obj.id} ({obj.type}) added to scene.")
def lock() -> Lock: """Creates lock with initialized scene and project.""" test = "test" lock = Lock({}) scene = UpdateableCachedScene(cmn.Scene(test, description=test)) lock.scene = scene project = UpdateableCachedProject(cmn.Project(test, lock.scene.id, description=test, has_logic=True)) lock.project = project assert lock.scene == scene assert lock.scene_or_exception() == scene assert lock.project == project assert lock.project_or_exception() == project # add some scene and project objects test_object = cmn.SceneObject(test, "TestType") lock.scene.upsert_object(test_object) ap = lock.project.upsert_action_point(cmn.BareActionPoint.uid(), "ap", cmn.Position(0, 0, 0)) ap_ap = lock.project.upsert_action_point(cmn.BareActionPoint.uid(), "ap_ap", cmn.Position(0, 0, 1), ap.id) ap_ap_ap = lock.project.upsert_action_point(cmn.BareActionPoint.uid(), "ap_ap_ap", cmn.Position(0, 0, 2), ap_ap.id) lock.project.upsert_action_point(cmn.BareActionPoint.uid(), "ap2", cmn.Position(0, 1, 0)) ori = cmn.NamedOrientation("ori", cmn.Orientation()) lock.project.upsert_orientation(ap_ap_ap.id, ori) action = cmn.Action("action", "test/type", parameters=[], flows=[]) lock.project.upsert_action(ap_ap_ap.id, action) return lock
async def new_scene_cb(req: srpc.s.NewScene.Request, ui: WsClient) -> None: """Creates and opens a new scene on the server. Fails if any scene is open or if scene id/name already exists. :param req: :return: """ if glob.PACKAGE_STATE.state in PackageState.RUN_STATES: raise Arcor2Exception("Can't create scene while package runs.") assert glob.SCENE is None for scene_id in (await storage.get_scenes()).items: if req.args.name == scene_id.name: raise Arcor2Exception("Name already used.") if req.dry_run: return None await get_object_types() # TODO not ideal, may take quite long time glob.SCENE = UpdateableCachedScene( common.Scene(req.args.name, desc=req.args.desc)) asyncio.ensure_future( notif.broadcast_event( sevts.s.OpenScene(sevts.s.OpenScene.Data(glob.SCENE.scene)))) asyncio.ensure_future(scene_srv.delete_all_collisions()) # just for sure return None
async def calibrate_camera(scene: UpdateableCachedScene, camera: Camera) -> None: assert camera.color_camera_params await notif.broadcast_event( ProcessState( ProcessState.Data(CAMERA_CALIB, ProcessState.Data.StateEnum.Started))) try: img = await run_in_executor(camera.color_image) estimated_pose = await run_in_executor( calib_client.estimate_camera_pose, camera.color_camera_params, img) except Arcor2Exception as e: await notif.broadcast_event( ProcessState( ProcessState.Data(CAMERA_CALIB, ProcessState.Data.StateEnum.Failed, str(e)))) glob.logger.exception("Failed to calibrate the camera.") return await update_scene_object_pose(scene, scene.object(camera.id), estimated_pose.pose, camera) await notif.broadcast_event( ProcessState( ProcessState.Data(CAMERA_CALIB, ProcessState.Data.StateEnum.Finished)))
async def delete_scene_cb( req: srpc.s.DeleteScene.Request, ui: WsClient) -> Optional[srpc.s.DeleteScene.Response]: if glob.LOCK.scene: raise Arcor2Exception("Scene has to be closed first.") user_name = glob.USERS.user_name(ui) async with ctx_write_lock(req.args.id, user_name, auto_unlock=req.dry_run): assoc_projects = await associated_projects(req.args.id) if assoc_projects: resp = srpc.s.DeleteScene.Response(result=False) resp.messages = ["Scene has associated projects."] resp.data = assoc_projects asyncio.create_task(glob.LOCK.write_unlock(req.args.id, user_name)) return resp if req.dry_run: return None scene = UpdateableCachedScene(await storage.get_scene(req.args.id)) asyncio.create_task(glob.LOCK.write_unlock(req.args.id, user_name)) await storage.delete_scene(req.args.id) evt = sevts.s.SceneChanged(scene.bare) evt.change_type = Event.Type.REMOVE asyncio.ensure_future(notif.broadcast_event(evt)) return None
async def new_scene_cb(req: srpc.s.NewScene.Request, ui: WsClient) -> None: """Creates and opens a new scene on the server. Fails if any scene is open or if scene id/name already exists. :param req: :return: """ async with glob.LOCK.get_lock(dry_run=req.dry_run): if glob.PACKAGE_STATE.state in PackageState.RUN_STATES: raise Arcor2Exception("Can't create scene while package runs.") if glob.LOCK.scene: raise Arcor2Exception("Scene has to be closed first.") for scene_id in await storage.get_scenes(): if req.args.name == scene_id.name: raise Arcor2Exception("Name already used.") if req.dry_run: return None await get_object_types() # TODO not ideal, may take quite long time glob.LOCK.scene = UpdateableCachedScene( common.Scene(req.args.name, description=req.args.description)) asyncio.ensure_future( notify_scene_opened( sevts.s.OpenScene(sevts.s.OpenScene.Data( glob.LOCK.scene.scene)))) asyncio.ensure_future( scene_srv.delete_all_collisions()) # just for sure return None
async def update_scene_object_pose( scene: UpdateableCachedScene, obj: SceneObject, pose: Optional[Pose] = None, obj_inst: Optional[GenericWithPose] = None, lock_owner: Optional[str] = None, ) -> None: """Performs all necessary actions when pose of an object is updated. :param obj: :param pose: :param obj_inst: :param lock_owner: if present, object is unlocked at the end of function :return: """ if pose: # SceneObject pose was not updated before obj.pose = pose else: # SceneObject pose was already updated pose = obj.pose scene.update_modified() evt = SceneObjectChanged(obj) evt.change_type = Event.Type.UPDATE await notif.broadcast_event(evt) glob.OBJECTS_WITH_UPDATED_POSE.add(obj.id) if scene_started(): if obj_inst is None: inst = get_instance(obj.id) assert isinstance(inst, GenericWithPose) obj_inst = inst assert pose is not None # Object pose is property that might call scene service - that's why it has to be called using executor. await hlp.run_in_executor(setattr, obj_inst, "pose", pose) if lock_owner: await glob.LOCK.read_unlock(obj.id, lock_owner)
def test_slots() -> None: """Tests whether classes from cached module uses __slots__.""" s = Scene("") p = Project("", s.id) assert not hasattr(CachedScene(s), "__dict__") assert not hasattr(CachedProject(p), "__dict__") assert not hasattr(UpdateableCachedScene(s), "__dict__") assert not hasattr(UpdateableCachedProject(p), "__dict__")
async def open_scene(scene_id: str) -> None: await asyncio.gather(scene_srv.delete_all_collisions(), get_object_types()) glob.LOCK.scene = UpdateableCachedScene(await storage.get_scene(scene_id)) try: for obj in glob.LOCK.scene.objects: check_object(glob.LOCK.scene, obj) except Arcor2Exception as e: glob.LOCK.scene = None raise Arcor2Exception(f"Failed to open scene. {str(e)}") from e
async def open_scene(scene_id: str) -> None: await get_object_types() asyncio.ensure_future(scene_srv.delete_all_collisions()) glob.SCENE = UpdateableCachedScene(await storage.get_scene(scene_id)) try: for obj in glob.SCENE.objects: check_object(obj) except Arcor2Exception as e: glob.SCENE = None raise Arcor2Exception(f"Failed to open scene. {str(e)}") from e
async def delete_scene_cb( req: srpc.s.DeleteScene.Request, ui: WsClient) -> Optional[srpc.s.DeleteScene.Response]: assoc_projects = await associated_projects(req.args.id) if assoc_projects: resp = srpc.s.DeleteScene.Response(result=False) resp.messages = ["Scene has associated projects."] resp.data = assoc_projects return resp if req.dry_run: return None scene = UpdateableCachedScene(await storage.get_scene(req.args.id)) await storage.delete_scene(req.args.id) evt = sevts.s.SceneChanged(scene.bare) evt.change_type = Event.Type.REMOVE asyncio.ensure_future(notif.broadcast_event(evt)) return None
async def test_ctx_read_lock() -> None: test = "test" user = "******" glob.LOCK = Lock({}) assert await glob.LOCK.get_locked_roots_count() == 0 glob.LOCK.scene = UpdateableCachedScene(cmn.Scene(test, description=test)) glob.LOCK.project = UpdateableCachedProject(cmn.Project(test, glob.LOCK.scene.id, description=test, has_logic=True)) async def patch() -> set[str]: return {glob.LOCK.project_or_exception().id, glob.LOCK.scene_or_exception().id} storage.get_project_ids = storage.get_scene_ids = patch # add some scene and project objects test_object = cmn.SceneObject(test, "TestType") glob.LOCK.scene.upsert_object(test_object) ap = glob.LOCK.project.upsert_action_point(cmn.BareActionPoint.uid(), "ap", cmn.Position(0, 0, 0), test_object.id) ap_ap = glob.LOCK.project.upsert_action_point(cmn.BareActionPoint.uid(), "ap_ap", cmn.Position(0, 0, 1), ap.id) assert await glob.LOCK.get_locked_roots_count() == 0 await glob.LOCK.write_lock(ap_ap.id, user, True) assert await glob.LOCK.is_write_locked(test_object.id, user) assert await glob.LOCK.is_write_locked(ap.id, user) assert await glob.LOCK.is_write_locked(ap_ap.id, user) async with ctx_read_lock(test_object.id, user): pass assert await glob.LOCK.is_write_locked(test_object.id, user) assert await glob.LOCK.is_write_locked(ap.id, user) assert await glob.LOCK.is_write_locked(ap_ap.id, user)
async def open_scene(scene_id: str) -> None: scene = await storage.get_scene(scene_id) if sp := await get_scene_problems( scene ): # this also refreshes ObjectTypes / calls get_object_types() logger.warning( f"Scene {scene.name} can't be opened due to the following problem(s)..." ) for spp in sp: logger.warning(spp) raise Arcor2Exception("Scene has some problems.") glob.LOCK.scene = UpdateableCachedScene(scene) def get_robot_instance( obj_id: str ) -> Robot: # TODO remove once https://github.com/python/mypy/issues/5374 is solved robot_inst = get_instance(obj_id, Robot) # type: ignore assert isinstance(robot_inst, Robot) return robot_inst T = TypeVar("T", bound=Generic) # TODO "thanks" to https://github.com/python/mypy/issues/3737, `expected_type: type[T] = Generic` can't be used