Skip to content

Conversation

@michaelosthege
Copy link
Contributor

When the first connection attempt does not succeed, the receive/jog threads must either be terminated, or they can not be started again when trying to (re)connect.

@michaelosthege
Copy link
Contributor Author

@cpr-bar I didn't get around to do other changes yet, but this prevented me from connecting reliably..

@cpr-bar
Copy link
Collaborator

cpr-bar commented Jan 28, 2026

Hi @michaelosthege,
I think there are multiple edge cases at work here.

You seemed to have hit a case in which the connection was terminaed but connected was not set to False, so the threads continued. This will be fixed by your code.

But there is also the case that the connection was terminated and the thread functions returned. In this case it is not possible to just call Thread.start() again.

I think the best to do is to always create a new Thread object when connecting instead of in __init__().

@michaelosthege
Copy link
Contributor Author

I went into the except ConnectionRefusedError: branch.

I pushed another commit that makes it better, but it's still not great, because the socket and the threads may or may not be alive when connect() goes into the exception branches.

If connect() would raise, it would be much easier.

Related: The current implementation does not explicitly handle disconnects.


Slightly related, coming back to the context manager idea, the following API might be quite useful:

client = CRIClient(ip, port)
assert not hasattr(client, "move_joints")  # client has no control methods

# for short-lived use:
async with client:  # connect, wait for first status update
    # TODO: read status
    assert client.connected is True
    print(client.robot_state)
# context exit: disconnect & dispose socket/threads

# for long-lived use
await client.connect()
# dome something
await client.disconnect()  # also disposes socket/threads

For active control:

# for long-lived use
await client.connect()

await with client.active_control() as controller:  # enable, reset, set_active_control, wait_for_kinematics_ready
    assert isinstance(controller, CRIController)
    assert isinstance(controller, CRIClient)
    await controller.move_joints(...)
# disable, set_active_control(False)

await client.disconnect()  # also disposes socket/threads

If I'm not mistaken, this could nicely handle the lifecycle for the three connection levels: disconnected, observing, controlling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants