Netherlands
Asked

Python Waitformove Timeout Solution

I am in the process of converting my EZ-Script code to Python and have encountered a notable difference in functionality. Here's a typical EZ-Script snippet I use to, for example, open a panel:

Servo(D4, 1)
WaitForServoMove(D4, 400)
Release(D4)

When transitioning to Python in ARC, the equivalent code looks like this:

try:
    Servo.SetPosition(D4, 1)
    Servo.WaitForMove(D4)  # Note: ARC Python does not accept a timeout argument
    Servo.Release(D4)

except Exception as e:
    log_event(logfilename, "P1", f"Error: {str(e)}", level="error")
    print("Error opening panel 1:", str(e))

The critical difference here is that the Python API lacks the timeout parameter for the WaitForMove function. This omission presents a potential issue: if the servo cannot move due to hardware failure or another reason, the script could hang indefinitely. This situation could lead to accumulation of stuck code if the script is called repeatedly.

Should I develop a custom solution, perhaps using multithreading in Python to address this issue? Or has someone already implemented a solution for this scenario? Is there an alternative approach available within ARC that resolves this problem differently?


Related Hardware EZ-B v4

ARC Pro

Upgrade to ARC Pro

Harnessing the power of ARC Pro, your robot can be more than just a simple automated machine.

PRO
Canada
#1  

Below is one approach you might consider. Since the ARC Python API’s Servo.WaitForMove() function does not accept a timeout, you can write your own "wait for move" with timeout logic. You might use a simple loop (or even a separate thread, if you wish to isolate the waiting process) to check the servo’s current position and break out if a timeout occurs.

Here’s an example of a custom wait_for_move_with_timeout() function that you can integrate into your code. (This example assumes you have some method-such as comparing the target position with the servo’s current position or a status variable-that lets you know when the movement is complete.)

For example:

import time

def wait_for_move_with_timeout(servo_port, target_position, timeout_ms=400):
    start_time = time.time()
    timeout_seconds = timeout_ms / 1000.0

    # Continuously check if the servo has reached the target position.
    # You'll need to define a small tolerance (e.g., 2 degrees) to account for mechanical variation.
    while True:
        current_position = Servo.GetPosition(servo_port)
        # Check if the servo is at (or nearly at) the target position.
        if abs(current_position - target_position) <= 2:
            # servo has reached destination
            break

        # If timeout has expired, exit the loop
        if (time.time() - start_time) > timeout_seconds:
            print(f"wait_for_move_with_timeout: Timed out on servo {servo_port}!")
            break

        # Sleep briefly so as not to hog the CPU
        time.sleep(0.01)

In your script, you could then replace the standard wait call with your custom function:

try:
    Servo.SetPosition(D4, 1)
    wait_for_move_with_timeout(D4, 1, timeout_ms=400)
    Servo.Release(D4)
except Exception as e:
    log_event(logfilename, P1, f"Error: {str(e)}", level="error")
    print("Error opening panel 1:", str(e))

A Few Points to Consider

  • Tolerance: In the helper function above, a tolerance of 2 degrees is used. You can adjust this based on how precise your servo positioning needs to be.
  • Custom vs. Multithreading:
    • Often a simple loop (as shown above) is sufficient if the script is not extremely time-critical or too many such waits occur simultaneously.
    • If you plan on having multiple servos or actions that might block one another, you might consider a multithreaded approach. In that case, a separate thread could monitor the servo and use a synchronization mechanism (like an event or a shared variable) to signal when the servo is done or the wait has timed out.
  • Alternative Approach: Instead of or in addition to a custom wait loop, consider whether you can use non-blocking scripting logic to check the state of the servo periodically. For example, if your overall application architecture supports asynchronous events, you might poll the servo’s status within your main loop.

While ARC does not currently provide a built-in timeout for WaitForMove in Python, this custom solution should allow you to prevent your script from hanging indefinitely in the event that the servo cannot move.

Feel free to ask if you need further clarification or additional tweaks!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#2  

I have updated the todo list for the next release to add a timeout to the Wait commands. This will be for Servo.wait* and Digital.wait* and ADC.wait*, etc...

PRO
Netherlands
#3  

My thanks to add a timeout to the wait commands for the next release! Really appreciated (and very useful). I will wait for the next release before updating those scripts to Python.