minute read

Command Queue in the Python API

You are reading our Automation1 API documentation for the Python™ programming language.

In the Python API, Command Queue lets you execute AeroScript commands on the Automation1 controller in a first-in, first-out process.

IMPORTANT: Before you read more about the command queue, read the Python API Commands page. This page adds to that information.

The Basics

When you execute non-queued AeroScript commands through the Python API, a quantity of fixed overhead occurs. There will be some delay between commands when you execute two of them, one right after the other. This delay averages from 3 to 5 milliseconds, based on the performance of the PC. It also includes all the network latency delays that might occur. If this delay is not compatible with your production environment, use Command Queue.

Command Queue lets you queue up multiple commands on the controller to prevent overhead and delay. Some AeroScript commands are available only in Command Queue because they cannot accept this overhead and delay. These commands include all the AeroScript commands that require Lookahead Synchronization, such as velocity blending, cutter radius compensation, and corner rounding. If you execute a series of MovePt() or MovePvt() commands, this process also requires a command queue. If you do not use one, your profile will stutter and drop out because of the delay that occurs between commands. If you are doing submillisecond PVTs, you must add the PVTs inside of a CriticalSectionStart() / CriticalSectionEnd() block. If you do not use the critical section functions, the motion will not be continuous. Refer to the Task Scheduling section of Task and Program Functions for more information.

A command queue on a task stores all the AeroScript commands that you add. It executes them sequentially, in the order that they were added, which is a first-in, first-out process. If you add AeroScript commands to the command queue, the Python function that you called to add the commands does not block unless the conditions that follow occur:

  • The command queue is full.
  • You specified the command queue to block when it is full.

If these two conditions do not occur, the AeroScript commands are added to the command queue. But all the commands that were previously added execute first. Then the AeroScript commands will execute.

Typically, a command queue is necessary for you to use when one of these use-cases apply:

  • Your process cannot accept the delay that occurs between executing AeroScript commands.
  • You want to use AeroScript commands that are compatible only with a command queue.

A command queue exists on a specific controller task. That task can execute only queued commands while the command queue is active. While a command queue is active, the restrictions that follow apply:

  • Non-queued AeroScript commands cannot execute.
  • AeroScript programs cannot run.
  • You cannot use retrace.
  • You cannot use interrupt motion.
  • You cannot use the AeroScript sync statement to execute a function on the controller task where a command queue exists. You can use the AeroScript sync statement to execute a function on a different task.
  • You cannot use the AeroScript statements that follow:
    • program statement
    • if statement
    • switch statement
    • goto statement
    • break statement
    • continue statement
    • while loop
    • for loop
    • foreach loop
    • repeat loop
  • You cannot declare an AeroScript variable, struct, enum, property, or function.
  • You cannot execute the AeroScript functions that follow:
    • ProgramStop()
    • ProgramPause()
    • ProgramReset()
    • ProgramRestart()
    • ProgramLoad()
    • ProgramStepInto()
    • ProgramStepOver()
    • ProgramStepOut()
  • AeroScript functions apply all relative paths as absolute paths.

How to Use

In the Python API, Command Queue is part of the Controller.runtime property. For all the APIs that operate under this property, the Automation1 controller must be running before you can do work with a command queue. For more information about this property, see the Controller page. If you reset the Automation1 controller, this process will stop all the active command queues.

To use a command queue, you must first begin a command queue on a specific task and with a specific AeroScript command capacity. The should_block_if_full argument controls the behavior of how an AeroScript command is added to a command queue that is full:

  • If you specify the argument as True, the add blocks until the AeroScript command can be added to the queue.
  • If you specify the argument as False, the add raises an exception.

Refer to the example that follows.

Copy
command_queue = controller.runtime.commands.begin_command_queue(task, command_capacity, should_block_if_full)

The command capacity specifies the maximum number of AeroScript commands that you can add to the command queue before it is full. When the command queue is full, you cannot continue to add new AeroScript commands. A larger command capacity uses more controller memory. The controller memory is limited.

WARNING: If you specify a command capacity that is too large, memory allocation errors can occur. If they occur, you must decrease your command capacity.

When the command queue begins on a specific task, non-queued AeroScript commands cannot execute and AeroScript programs cannot run on that task.

Also, the execution of commands is not paused. Thus, AeroScript commands that you add to the queue will immediately start to execute and the state of the task becomes Queue Running. If you enable the feedhold state on the task, the state of the task continues as Queue Running.

To pause the execution of commands in the command queue, use the pause method. This method pauses the execution of commands from the command queue after the controller completes execution of the current command and the state of the task becomes Queue Paused. Refer to the example that follows.

Copy
command_queue.pause()

If the execution of commands is paused in the command queue, you can use the resume method to resume the execution of commands. Refer to the example that follows.

Copy
command_queue.resume()

The CommandQueue object has the commands property. You can use this property to add AeroScript commands to the command queue. When you add AeroScript commands, they are not executed immediately. Instead, they are queued for execution. All the commands that you previously added are executed first. Then the newest AeroScript commands that you added will execute. These methods do not block unless the conditions that follow occur:

  • The command queue is full.
  • You specified the command queue to block when it is full.

Refer to the example that follows.

Copy
command_queue.commands.motion.enable(axes)
command_queue.commands.motion.movelinear(axes, distances, coordinated_speed)
command_queue.commands.motion.disable(axes)

To see a list of all the Python properties and methods that you can use for Command Queue, refer to the Full Reference section.

While the command queue is active, you can examine its status to find the information that follows:

  • The number of AeroScript commands that were executed.
  • The number of remaining AeroScript commands in the command queue that must be executed.

Refer to the example that follows.

Copy
command_queue.status.is_empty
command_queue.status.number_of_executed_commands
command_queue.status.number_of_unexecuted_commands

Each time that you access the status property, you get a new status from the controller. For best results, access the status property one time only to get the most current status. Then use the returned CommandQueueStatus object until you need to get the most current status of the controller again. If you do not do this and try to access the status property too many times, bugs known as race conditions can occur. These bugs occur when the status of the task changes between multiple accesses of the property.

If you want to wait for the command queue to empty and complete its execution of all the commands that you added to it, use the wait_for_empty method. This method blocks until the command queue is fully empty. Then this method returns and your code execution continues. Refer to the example that follows.

Copy
command_queue.wait_for_empty()

When you are done using the command queue, call the end_command_queue method to end the command queue and return the task to its normal state. Refer to the example that follows.

Copy
controller.runtime.commands.end_command_queue(command_queue)

If you end a command queue that is currently executing a command, that command is aborted and all the queued AeroScript commands are discarded. To prevent this from occurring, use the wait_for_empty method when you end the command queue. This method makes sure that the command queue is not executing commands.

After you end the command queue, non-queued AeroScript commands can execute and AeroScript programs can run on that task. You cannot continue to use the CommandQueue object. To make a new command queue, you must call the begin_command_queue method again.

Execute Custom AeroScript

You can execute most AeroScript commands through the Python API. In some special cases, you must execute custom AeroScript directly or execute a file that contains the custom AeroScript.

Execute Custom AeroScript Directly

To execute custom AeroScript directly, use the CommandQueue.commands.execute method to specify the custom AeroScript that you want to add to the command queue. Refer to the example that follows.

Copy
command_queue.commands.execute(aeroscript_text)

The aeroscript_text parameter can include one or more lines of AeroScript. You can separate lines of AeroScript with newline characters, which include CR, LF, and CRLF. If the string has one or more lines, the TaskStatusItem.CommandQueueLineNumber status item will be incremented by the number of lines in the string, but the TaskStatusItem.CommandQueueExecutedCount status item and the CommandQueueStatus.number_of_executed_commands property will be incremented by 1. This occurs because there is a difference between lines of AeroScript and a command that has one or more lines of AeroScript.

Execute Custom AeroScript from a File

You can make the Python API read from a file and automatically put lines of AeroScript as commands into a command queue. You can do this with an MDK file or a controller file. This process can help you if you currently have a file that includes all of your AeroScript or if you want to separate your AeroScript process from your custom Python application. The specified file can include simple lines of AeroScript only. The file cannot include variables, if statements, loops, or other control flow statements.

To execute an MDK file that has lines of AeroScript, use the CommandQueue.commands.execute_from_mdk_file method. To execute a controller file that has lines of AeroScript, use the CommandQueue.commands.execute_from_controller_file method. For more information about the MDK and controller file systems, see Working with Files.

Aerotech recommends that you set the CommandQueue.should_block_if_full argument to True when you create a command queue that is executed from a file. If it is set to False and the command queue fills while reading the file, a ControllerOperationException will be raised.

To increase throughput, lines of AeroScript from the file are added to the command queue as commands that have one or more lines. The number_of_lines_per_command argument specifies the number of file lines to read from the file for each command. By default, the number_of_lines_per_command argument is set to 1000, which is applicable most of the time. If the command queue is empty before the file is fully read, set the number_of_lines_per_command argument to more than 1000. The value of this argument depends on the performance of your PC and is not deterministic.

WARNING: If you specify a value for the number_of_lines_per_command argument that is too large, memory allocation errors can occur. If you get memory allocation errors, decrease the value of this argument.

When the CommandQueue.commands.execute_from_mdk_file or the CommandQueue.commands.execute_from_controller_file method returns, there can be remaining lines of AeroScript from the file that were not executed and are still in the command queue. Use the CommandQueue.wait_for_empty method to make sure that all of the AeroScript commands fully execute. Refer to the example that follows.

Copy
command_queue = controller.runtime.commands.begin_command_queue("Task 1", 100, True)
command_queue.commands.execute_from_mdk_file("MovePvt20kHz.txt", 1000)
command_queue.wait_for_empty()

If the controller executes blank lines on the command queue, this will not cause an exception.

AeroScript Commands

You can use AeroScript commands to change the state of a command queue.

If the execution of queued AeroScript commands from the command queue is not paused at this time, use the AeroScript functions that follow to pause the execution of commands from the queue.

CommandQueuePause()
CommandQueuePause($task as integer)

If the execution of queued AeroScript commands from the command queue is currently paused, use the AeroScript function that follows to continue the execution of commands from the queue.

CommandQueueStart($task as integer)

Use the AeroScript functions that follow to stop the command queue.

CommandQueueStop()
CommandQueueStop($task as integer)

Keep a Command Queue Full

WARNING: You must keep the command queue populated at all times. If the command queue is not populated, your process will stall and motion problems might occur.

When the command queue begins on a task, the controller executes all the commands that are in the command queue as quickly as possible. If the controller automatically executes commands from the command queue more quickly than you can add them, the command queue will not have a sufficient quantity of commands to execute. This condition is known as a starvation of the command queue.

While the command queue is active, you can examine its status to find the number of times that a starvation of the command queue has occurred. Refer to the example that follows.

Copy
command_queue.status.number_of_times_emptied

For the axes that you specify to a command, the controller automatically decelerates them to zero velocity to prevent motion problems from occurring when all the conditions that follow are true:

  • Velocity Blending mode is enabled on the task.
  • A starvation of the command queue occurs.
  • The most recently executed command from the command queue is a MoveCcw(), MoveCw(), or MoveLinear() command.

But when the two conditions that follow are true, the controller does not automatically decelerate the axes that you specify to a command to zero velocity:

  • A starvation of the command queue occurs.
  • The most recently executed command from the command queue is a MovePt() or MovePvt() command.

As a result, motion problems can occur.

To prevent a starvation of the command queue, do one or more of the options that follow:

  • Issue a Dwell() command as the first command. Also issue it in other important locations. When you issue the Dwell() command in multiple locations, you get more time to add commands to the command queue before the controller executes them.
  • Pause the command queue before you add more commands. You can pause the command queue by using the CommandQueue.pause() method. You can also issue a CommandQueuePause() command as the first command.
  • Issue a wait statement first. Then use the CommandQueueCount task status item to make the first command wait for the command queue to fill with a specified number of commands before the controller executes them. Refer to the code sample that follows:
Copy
command_queue.execute("wait(StatusGetTaskItem ... >= 20)")

Example Code

For more information about how to use a command queue, refer to the example program that follows.

Copy
# Begin a new command queue on task 1.
command_queue = controller.runtime.commands.begin_command_queue("Task 1", 10, True)

# First, pause the command queue so you can add all the commands
# before they are executed.
command_queue.pause()

# Add all the AeroScript commands that you want to execute.
command_queue.commands.advanced_motion.velocityblendingon()
command_queue.commands.motion.enable("X")
command_queue.commands.motion.movelinear("X", [10], 5)
command_queue.commands.motion.movelinear("X", [5], 5)
command_queue.commands.motion.disable("X")
command_queue.commands.advanced_motion.velocityblendingoff()

# Resume the command queue so that all the commands that you added start
# to execute.
command_queue.resume()

# Here you can do other things such as more process, get status, etc.
# You can do these things because the command queue is executing
# commands on the controller and is not blocking your code execution.

# Here you wait to make sure that the command queue executes all the commands.
# You must do this before you end the command queue.
# When you end the command queue, this process aborts all motion and commands.
command_queue.wait_for_empty()

# At this time, end the command queue.
# You can also call CommandQueue.end_command_queue to abort the command
# that is currently executing and discard the remaining commands.
controller.runtime.commands.end_command_queue(command_queue)

Thread Safety

The threading library is the only multithreading library that is officially supported by the Automation1 Python API.

The Controller.runtime.commands.begin_command_queue method is thread safe. You can call it from two or more threads without interference.

The Controller.runtime.commands.end_commmand_queue method is not thread safe. Only one thread should call this function for a single CommandQueue instance.

The CommandQueue class is not thread safe. Only one thread at a time can access an instance of this class. If it is necessary for two or more threads to access or change an instance of this class, you must use locks to limit the access. You must also use locks when you pass a CommandQueue instance to the Controller.runtime.commands.end_command_queue method. If it is necessary for two or more threads to access or change an instance of the CommandQueue class, you must use locks to limit the access.

Full Reference

For more information about the properties and methods that are available for Command Queue, refer to the lists that follow.

CommandQueue Properties

CommandQueue Methods

CommandQueue.status Properties

CommandQueue.commands Methods