Exemple #1
0
async def project_manager_client(handle_manager_incoming_messages) -> None:

    while True:

        logger.info("Attempting connection to manager...")

        try:

            async with websockets.connect(EXE_URL) as manager_client:  # type: ignore  # TODO not sure what is wrong

                logger.info("Connected to manager.")

                future = asyncio.ensure_future(handle_manager_incoming_messages(manager_client))

                while True:

                    if future.done():
                        break

                    try:
                        msg = await asyncio.wait_for(MANAGER_RPC_REQUEST_QUEUE.get(), 1.0)
                    except asyncio.TimeoutError:
                        continue

                    try:
                        await manager_client.send(msg.to_json())
                    except websockets.exceptions.ConnectionClosed:
                        await MANAGER_RPC_REQUEST_QUEUE.put(msg)
                        break
        except ConnectionRefusedError as e:
            logger.error(e)
            await asyncio.sleep(delay=1.0)
Exemple #2
0
async def object_aiming_done_cb(req: srpc.o.ObjectAimingDone.Request,
                                ui: WsClient) -> None:
    """Calls scene service to get a new pose for the object.

    In case of success, robot and object are kept locked, unlocking is responsibility of ui.
    On failure, UI may do another attempt or call ObjectAimingCancel.

    :param req:
    :param ui:
    :return:
    """

    scene = glob.LOCK.scene_or_exception()
    fo, user_name = await object_aiming_check(ui)

    obj_type = glob.OBJECT_TYPES[scene.object(fo.obj_id).type].meta
    assert obj_type.object_model
    assert obj_type.object_model.mesh

    focus_points = obj_type.object_model.mesh.focus_points

    assert focus_points

    if len(fo.poses) < len(focus_points):
        raise Arcor2Exception(
            f"Only {len(fo.poses)} points were done out of {len(focus_points)}."
        )

    obj = scene.object(fo.obj_id)
    assert obj.pose

    obj_inst = get_instance(fo.obj_id, CollisionObject)

    if req.dry_run:
        return

    fp: list[Position] = []
    rp: list[Position] = []

    for idx, pose in fo.poses.items():

        fp.append(focus_points[idx].position)
        rp.append(pose.position)

    mfa = MeshFocusAction(fp, rp)

    logger.debug(f"Attempt to aim object {obj_inst.name}, data: {mfa}")

    try:
        new_pose = await scene_srv.focus(mfa)  # TODO how long does it take?
    except scene_srv.SceneServiceException as e:
        logger.error(f"Aiming failed with: {e}, mfa: {mfa}.")
        raise Arcor2Exception(f"Aiming failed. {str(e)}") from e

    logger.info(f"Done aiming for {obj_inst.name}.")

    _objects_being_aimed.pop(user_name, None)
    asyncio.create_task(
        update_scene_object_pose(scene, obj, new_pose, obj_inst))
    return None
Exemple #3
0
async def robot_joints_event(robot_inst: Robot) -> None:

    logger.info(f"Sending joints for {robot_inst.name} started.")
    while scene_started() and glob.ROBOT_JOINTS_REGISTERED_UIS[robot_inst.id]:

        start = time.monotonic()

        try:
            evt = RobotJoints(RobotJoints.Data(robot_inst.id, (await robot.get_robot_joints(robot_inst, None, True))))
        except Arcor2Exception as e:
            logger.error(f"Failed to get joints for {robot_inst.name}. {str(e)}")
            await asyncio.sleep(1)
            continue

        evt_json = evt.to_json()
        await asyncio.gather(
            *[ws_server.send_json_to_client(ui, evt_json) for ui in glob.ROBOT_JOINTS_REGISTERED_UIS[robot_inst.id]]
        )

        end = time.monotonic()
        await asyncio.sleep(EVENT_PERIOD - (end - start))

    ROBOT_JOINTS_TASKS.pop(robot_inst.id, None)

    # TODO notify UIs that registration was cancelled
    glob.ROBOT_JOINTS_REGISTERED_UIS.pop(robot_inst.id, None)

    logger.info(f"Sending joints for {robot_inst.name} stopped.")
Exemple #4
0
async def object_aiming_start_cb(req: srpc.o.ObjectAimingStart.Request,
                                 ui: WsClient) -> None:
    """Starts the aiming process for a selected object (with mesh) and robot.

    Only possible when the scene is started/online.
    UI have to acquire write locks for object and robot in advance.
    :param req:
    :param ui:
    :return:
    """

    scene = glob.LOCK.scene_or_exception()

    if glob.LOCK.project:
        raise Arcor2Exception("Project has to be closed first.")

    ensure_scene_started()

    user_name = glob.USERS.user_name(ui)

    if user_name in _objects_being_aimed:
        raise Arcor2Exception("Aiming already started.")

    obj_id = req.args.object_id
    scene_obj = scene.object(obj_id)

    obj_type = glob.OBJECT_TYPES[scene_obj.type].meta

    if not obj_type.has_pose:
        raise Arcor2Exception("Only available for objects with pose.")

    if not obj_type.object_model or obj_type.object_model.type != Model3dType.MESH:
        raise Arcor2Exception("Only available for objects with mesh model.")

    assert obj_type.object_model.mesh

    focus_points = obj_type.object_model.mesh.focus_points

    if not focus_points:
        raise Arcor2Exception("focusPoints not defined for the mesh.")

    await ensure_write_locked(req.args.object_id, user_name)
    await ensure_write_locked(req.args.robot.robot_id, user_name)

    await check_eef_arm(get_robot_instance(req.args.robot.robot_id),
                        req.args.robot.arm_id, req.args.robot.end_effector)

    if req.dry_run:
        return

    _objects_being_aimed[user_name] = AimedObject(req.args.object_id,
                                                  req.args.robot)
    logger.info(
        f"{user_name} just started aiming of {scene_obj.name} using {scene.object(req.args.robot.robot_id).name}."
    )
Exemple #5
0
    async def _start_scene() -> bool:

        logger.info(f"Starting the {scene.name} scene.")

        await set_scene_state(SceneState.Data.StateEnum.Starting)

        # in order to prepare a clear environment
        try:
            # stop deletes all configurations and clears all collisions
            await scene_srv.stop()
        except Arcor2Exception:
            logger.exception("Failed to prepare for start.")
            await set_scene_state(SceneState.Data.StateEnum.Stopped,
                                  "Failed to prepare for start.")
            return False

        object_overrides: dict[str, list[Parameter]] = {}

        if glob.LOCK.project:
            object_overrides = glob.LOCK.project.overrides

        # object initialization could take some time - let's do it in parallel
        tasks = [
            asyncio.ensure_future(
                create_object_instance(
                    obj, object_overrides[obj.id]
                    if obj.id in object_overrides else None))
            for obj in scene.objects
        ]

        try:
            await asyncio.gather(*tasks)
        except Arcor2Exception as e:
            for t in tasks:
                t.cancel()  # TODO maybe it would be better to let them finish?
            logger.exception("Failed to create instances.")
            await stop_scene(scene, str(e), already_locked=True)
            return False

        try:
            await scene_srv.start()
        except Arcor2Exception as e:
            logger.exception("Failed to go online.")
            await stop_scene(scene, str(e), already_locked=True)
            return False

        await set_scene_state(SceneState.Data.StateEnum.Started)
        return True
Exemple #6
0
async def object_aiming_prune() -> None:
    """Deletes records for users that already lost their locks.

    :return:
    """

    to_delete: list[str] = []

    # users in db but not holding a lock for the object should be deleted
    for un, fo in _objects_being_aimed.items():

        if not await glob.LOCK.is_write_locked(fo.obj_id, un):
            logger.info(f"Object aiming cancelled for {un}.")
            to_delete.append(un)

    for td in to_delete:
        _objects_being_aimed.pop(td, None)
Exemple #7
0
async def robot_eef_pose_event(robot_inst: Robot) -> None:

    eefs: dict[Optional[str], set[str]] = {}

    try:
        try:
            for arm_id in await robot.get_arms(robot_inst):
                eefs[arm_id] = await robot.get_end_effectors(robot_inst, arm_id)
        except robot.SingleArmRobotException:
            eefs = {None: await robot.get_end_effectors(robot_inst, None)}
    except Arcor2Exception as e:
        logger.error(f"Failed to start sending poses for {robot_inst.name}. {str(e)}")
    else:

        logger.info(f"Sending poses for {robot_inst.name} started. Arms/EEFs: {eefs}.")

        while scene_started() and glob.ROBOT_EEF_REGISTERED_UIS[robot_inst.id]:

            start = time.monotonic()

            evt = RobotEef(RobotEef.Data(robot_inst.id))

            try:
                evt.data.end_effectors = await asyncio.gather(
                    *[eef_pose(robot_inst, eef_id, arm_id) for arm_id in eefs.keys() for eef_id in eefs[arm_id]]
                )
            except Arcor2Exception as e:
                logger.error(f"Failed to get eef pose for {robot_inst.name}. {str(e)}")
                await asyncio.sleep(1)
                continue

            evt_json = evt.to_json()
            await asyncio.gather(
                *[ws_server.send_json_to_client(ui, evt_json) for ui in glob.ROBOT_EEF_REGISTERED_UIS[robot_inst.id]]
            )

            end = time.monotonic()
            await asyncio.sleep(EVENT_PERIOD - (end - start))

    EEF_POSE_TASKS.pop(robot_inst.id, None)

    # TODO notify UIs that registration was cancelled
    glob.ROBOT_EEF_REGISTERED_UIS.pop(robot_inst.id, None)

    logger.info(f"Sending poses for {robot_inst.name} stopped.")
Exemple #8
0
async def object_aiming_cancel_cb(req: srpc.o.ObjectAimingCancel.Request,
                                  ui: WsClient) -> None:
    """Cancel aiming of the object.

    :param req:
    :param ui:
    :return:
    """

    fo, user_name = await object_aiming_check(ui)

    if req.dry_run:
        return

    _objects_being_aimed.pop(user_name, None)
    if glob.LOCK.scene:
        logger.info(
            f"Aiming for {glob.LOCK.scene.object(fo.obj_id).name} cancelled by {user_name}."
        )
Exemple #9
0
async def run_temp_package(package_id: str, start_paused: bool = False, breakpoints: Optional[set[str]] = None) -> None:

    # TODO lock scene and project?

    assert glob.LOCK.scene
    assert glob.LOCK.project
    project_id = glob.LOCK.project.id
    glob.TEMPORARY_PACKAGE = True

    scene_online = scene_started()

    if scene_online:
        await stop_scene(glob.LOCK.scene)  # the package will start it on its own

    await project.close_project(show_mainscreen_after_that=False)
    req = erpc.RunPackage.Request
    exe_req = req(get_id(), args=req.Args(package_id, start_paused, breakpoints))
    exe_resp = await manager_request(exe_req)

    if not exe_resp.result:
        logger.warning(f"Execution of temporary package failed with: {exe_resp.messages}.")
    else:
        await server_events.package_started.wait()
        await server_events.package_stopped.wait()
        logger.info("Temporary package stopped, let's remove it and reopen project.")

    glob.TEMPORARY_PACKAGE = False

    await manager_request(erpc.DeletePackage.Request(get_id(), args=rpc.common.IdArgs(package_id)))

    await project.open_project(project_id)

    assert glob.LOCK.scene
    assert glob.LOCK.project

    await notif.broadcast_event(
        sevts.p.OpenProject(sevts.p.OpenProject.Data(glob.LOCK.scene.scene, glob.LOCK.project.project))
    )

    if scene_online:
        await start_scene(glob.LOCK.scene)
Exemple #10
0
async def remove_object_references_from_projects(obj_id: str) -> None:

    assert glob.LOCK.scene

    updated_project_ids: set[str] = set()

    async for project in projects_using_object_as_parent(
            glob.LOCK.scene.id, obj_id):

        # action_ids: set[str] = set()

        # delete actions using the object
        for action_to_delete in {
                act.id
                for act in project.actions if act.parse_type()[0] == obj_id
        }:
            project.remove_action(action_to_delete)

        # delete actions using obj's action points as parameters
        # TODO fix this!
        """
        actions_using_invalid_param: set[str] = \
            {act.id for act in ap.actions for param in act.parameters
             if param.type in (ActionParameterTypeEnum.JOINTS, ActionParameterTypeEnum.POSE) and
             param.value.startswith(obj_id)}

        ap.actions = [act for act in ap.actions if act.id not in actions_using_invalid_param]

        # get IDs of remaining actions
        action_ids.update({act.id for act in ap.actions})
        """

        # valid_ids: set[str] = action_ids | ActionIOEnum.set()

        # TODO remove invalid logic items

        await storage.update_project(project)
        updated_project_ids.add(project.id)

    logger.info("Updated projects: {}".format(updated_project_ids))
Exemple #11
0
async def object_aiming_add_point_cb(
        req: srpc.o.ObjectAimingAddPoint.Request,
        ui: WsClient) -> srpc.o.ObjectAimingAddPoint.Response:

    scene = glob.LOCK.scene_or_exception()
    fo, user_name = await object_aiming_check(ui)

    pt_idx = req.args.point_idx
    scene_obj = scene.object(fo.obj_id)
    obj_type = glob.OBJECT_TYPES[scene_obj.type].meta

    assert obj_type.has_pose
    assert obj_type.object_model
    assert obj_type.object_model.mesh

    focus_points = obj_type.object_model.mesh.focus_points

    assert focus_points

    if pt_idx < 0 or pt_idx > len(focus_points) - 1:
        raise Arcor2Exception("Index out of range.")

    robot_id, end_effector, arm_id = fo.robot.as_tuple()

    robot_inst = get_robot_instance(robot_id)

    r = srpc.o.ObjectAimingAddPoint.Response()
    r.data = r.Data(finished_indexes=list(fo.poses.keys()))

    if not req.dry_run:
        fo.poses[pt_idx] = await get_end_effector_pose(robot_inst,
                                                       end_effector, arm_id)
        r.data = r.Data(finished_indexes=list(fo.poses.keys()))
        logger.info(
            f"{user_name} just aimed index {pt_idx} for {scene_obj.name}. Done indexes: {r.data.finished_indexes}."
        )

    return r
Exemple #12
0
async def start_scene(scene: CachedScene) -> None:
    """Creates instances of scene objects."""
    async def _start_scene() -> bool:

        logger.info(f"Starting the {scene.name} scene.")

        await set_scene_state(SceneState.Data.StateEnum.Starting)

        # in order to prepare a clear environment
        try:
            # stop deletes all configurations and clears all collisions
            await scene_srv.stop()
        except Arcor2Exception:
            logger.exception("Failed to prepare for start.")
            await set_scene_state(SceneState.Data.StateEnum.Stopped,
                                  "Failed to prepare for start.")
            return False

        object_overrides: dict[str, list[Parameter]] = {}

        if glob.LOCK.project:
            object_overrides = glob.LOCK.project.overrides

        # object initialization could take some time - let's do it in parallel
        tasks = [
            asyncio.ensure_future(
                create_object_instance(
                    obj, object_overrides[obj.id]
                    if obj.id in object_overrides else None))
            for obj in scene.objects
        ]

        try:
            await asyncio.gather(*tasks)
        except Arcor2Exception as e:
            for t in tasks:
                t.cancel()  # TODO maybe it would be better to let them finish?
            logger.exception("Failed to create instances.")
            await stop_scene(scene, str(e), already_locked=True)
            return False

        try:
            await scene_srv.start()
        except Arcor2Exception as e:
            logger.exception("Failed to go online.")
            await stop_scene(scene, str(e), already_locked=True)
            return False

        await set_scene_state(SceneState.Data.StateEnum.Started)
        return True

    to_lock = [glob.LOCK.SpecialValues.SCENE]

    if glob.LOCK.project:
        to_lock.append(glob.LOCK.SpecialValues.PROJECT)

    try:
        async with ctx_write_lock(to_lock, glob.LOCK.Owners.SERVER):
            ret = await _start_scene()
    except CannotLock:
        logger.warning(
            f"Failed attempt to start the scene. Can't lock {to_lock}.")
        return
    except Arcor2Exception as e:
        logger.error(f"Failed to start the scene. {str(e)}")

    assert ret == scene_started()
    assert ret == await scene_srv.started()

    if ret:
        logger.info("Scene started. Enjoy!")
Exemple #13
0
async def stop_scene(scene: CachedScene,
                     message: Optional[str] = None,
                     already_locked: bool = False) -> None:
    """Destroys scene object instances."""
    async def _stop_scene() -> None:

        await set_scene_state(SceneState.Data.StateEnum.Stopping, message)

        try:
            await scene_srv.stop()
        except Arcor2Exception as e:
            logger.exception("Failed to go offline.")
            await set_scene_state(SceneState.Data.StateEnum.Started, str(e))
            return

        try:
            await asyncio.gather(*[
                cleanup_object(obj)
                for obj in glob.SCENE_OBJECT_INSTANCES.values()
            ])
        except Arcor2Exception as e:
            logger.exception("Exception occurred while cleaning up objects.")
            await set_scene_state(SceneState.Data.StateEnum.Stopped, str(e))
        else:
            await set_scene_state(SceneState.Data.StateEnum.Stopped)

        glob.SCENE_OBJECT_INSTANCES.clear()
        glob.PREV_RESULTS.clear()

    if already_locked:

        logger.info(
            f"Stopping the {scene.name} scene after unsuccessful start.")

        assert await glob.LOCK.is_write_locked(glob.LOCK.SpecialValues.SCENE,
                                               glob.LOCK.Owners.SERVER)
        assert (glob.LOCK.project
                is not None) == await glob.LOCK.is_write_locked(
                    glob.LOCK.SpecialValues.PROJECT, glob.LOCK.Owners.SERVER)

        await _stop_scene()
    else:

        to_lock = [glob.LOCK.SpecialValues.SCENE]

        if glob.LOCK.project:
            to_lock.append(glob.LOCK.SpecialValues.PROJECT)

        try:
            async with ctx_write_lock(to_lock, glob.LOCK.Owners.SERVER):
                logger.info(f"Stopping the {scene.name} scene.")
                await _stop_scene()
        except CannotLock:
            logger.warning(
                f"Failed attempt to stop the scene. Can't lock {to_lock}.")
            return
        except Arcor2Exception as e:
            logger.error(f"Failed to stop the scene. {str(e)}")
            return

    assert not scene_started()
    assert not await scene_srv.started()

    logger.info("Scene stopped.")