EtherCAT URScript
ethercat_is_service_responsive
ethercat_is_service_responsive(timeout = 0.5, server_port = 0, server_host = "")
Description
Checks if the MotionPlus Service is responsive
This function returns True if the MotionPlus Service responds to a GetStatus XML-RPC call within the provided timeout
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
timeout |
float |
Y |
Seconds to wait for method result before failing with error. Default: 0.5 |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
bool where True indicates the MotionPlus Service is responsive
Example
ret = ethercat_is_service_responsive() # would be True, if the service is responsive
ethercat_set_parameter
ethercat_set_parameter(key, value, timeout = 1.0, server_port = 0, server_host = "")
Description
Sets EtherCAT Master parameter
This function will halt the program with an error, if the EtherCAT Master is already running, or if the key or the value is invalid
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
key |
string |
N |
Name of EtherCAT Master parameter to set. |
value |
bool, int, float, or string |
N |
New value for the EtherCAT Master parameter. The type of this argument depends on the value of |
timeout |
float |
Y |
Timeout in seconds to wait for the operation to finish. Defaults to 1. |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
The following EtherCAT Master parameters are supported by this function:
Key |
Value Type |
Default Value |
Description |
---|---|---|---|
dc_enable |
bool |
False |
Whether EtherCAT Distributed Clock should be enabled. |
Return
none
Example
In this example, the EtherCAT Distributed Clock is disabled
ethercat_set_parameter("dc_enable", False)
ethercat_config_axis
ethercat_config_axis(axis_name, station_address, counts_per_rev, gear_ratio, feed_constant, zero_offset_count, axis_index = -1, timeout = 2.0, server_port = 0, server_host = "")
Description
Configures the axis with parameters of the corresponding hardware
This function must be called for each axis separately
This function will halt the program with an error, if the value of
axis_name
doesn’t match an axis name provided toaxis_group_add()
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
Name of the axis. This should match an axis name provided to axis_group_add(). |
station_address |
int |
N |
EtherCAT station address for the axis. The value must be greater than 0 and no larger than 65536. |
counts_per_rev |
int |
N |
Increment in encoder counts for each round the motor turns. The value must be greater than 0. |
gear_ratio |
float |
N |
Gear ratio for the axis. |
feed_constant |
float |
N |
Feed constant for the axis in radians. Usually, it’s 2*PI (PI=3.14159265359) for rotary positioners. |
zero_offset_count |
int |
N |
Encoder reading in counts when the axis is at 0 position. The joint angle = ((current encoder – offset) * feed_constant) / (counts_per_rev * gear_ratio). |
axis_index |
int |
Y |
Index for the axis in the joint positions vector. Defaults to |
timeout |
float |
Y |
Timeout in seconds to wait for the operation to finish. Defaults to |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
none
Examples
In this example, a new axis group and axis are added and the new axis is configured to be commanded with EtherCAT
Note: replace the values for <ENCODER>
and <GEAR>
to match your servo setup.
global PI = acos(-1)
AXIS_TYPE = 0 # 0:revolute, 1:prismatic
VELOCITY_LIMIT = 1 # rad/s
ACCELERATION_LIMIT = 10 # rad/s^2
ENCODER_RESOLUTION = <ENCODER> # Specify encoder ticks per revolution of the motor
GEAR_RATIO = <GEAR> # Specify gear after motor output
FEED_CONSTANT = 2 * PI # change sign to change motor direction
ZERO_OFFSET = 0
#create new axis group and axis
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
# configure the axis to be controlled via ethercat
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_is_master_running
ethercat_is_master_running(timeout = 2.0, server_port = 0, server_host = "")
Description
Checks whether the EtherCAT Master is running
This function will halt the program with an error, if it times out, or if an error is encountered
This function requires the MotionPlus Service to be running
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
timeout |
float |
Y |
Seconds to wait for heartbeat (Status) before failing with error. Default: 2.0 |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
bool result indicating whether the EtherCAT Master is currently running
Example
# assuming ethercat start/stop is handled on a different thread
if ethercat_is_master_running():
# do stuff if master is running
else:
# do stuff if master is not running
end
ethercat_start
ethercat_start(timeout = 5.0, server_port = 0, server_host = "")
Description
Starts the EtherCAT Master communication with the slave axes configured through calls of ethercat_config_axis()
and waits for the EtherCAT Master network state to be Operational (OP)
This function will halt the program and display an error popup on PolyScope, if it times out, or if an error is encountered (e.g. if initialization or configuration fails during startup)
This function requires one axis to be defined, and the MotionPlus service running
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
timeout |
float |
Y |
Timeout in seconds to wait for EtherCAT master state to be Operational (OP). Default: 5.0. |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
none
Example
In this example, a new axis group and axis are added and the new axis is configured to be commanded with EtherCAT. The EtherCAT master is then started using the default startup wait.
# create an axis group
axis_group_add("groupA", tf, "world")
# create a new axis
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
# configure the axis to be controlled via ethercat
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
# starts up ethercat master
ethercat_start()
ethercat_stop
ethercat_stop(reset = False, timeout = 5.0, server_port = 0, server_host = "")
Description
Stops the EtherCAT Master
This function will halt the program and display an error popup on PolyScope, if it times out, or if an error is encountered
This function can be called whether or not the EtherCAT Master has already been started
This function can also remove all the slaves (axes) configured via ethercat_config_axis(). This option is useful to ensure the EtherCAT network begins in a clean, unconfigured state
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
reset |
bool |
Y |
Resets the axis configuration via ResetConfiguration method. Default: False |
timeout |
float |
Y |
Timeout in seconds to wait for EtherCAT master to finish stopping. Default: 5.0 |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
none
Example
ethercat_stop(True) # make sure the ethercat master is stopped and clear the configured ethercat slaves... this makes the program start with a clean slate
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start() # starts up ethercat
# do stuff
ethercat_stop() # stop the ethercat master only... don't clear the ethercat slaves
ethercat_begin_status_monitor
ethercat_begin_status_monitor(period = 0.1, max_missed_messages = 3, timeout = 1.0, server_port = 0, server_host = "")
Description
Starts background task to periodically call
GetStatus
XML-RPC method
This function will halt the program, if the responses stop or are never received
This function will halt the program, if any response contains a non-zero error code
This is useful for making a URScript program identify if the MotionPlus Service has prematurely quit or encountered a runtime error
Note: Runtime errors are those that prevent the MotionPlus Service from functioning properly. They do not include faults from the EtherCAT slaves
This function will halt the program, if another monitoring task is already running
Only one Status Monitor task may be running at any given time
Call ethercat_end_status_monitor() to stop the previous task, before starting a new one
Monitoring will automatically start when program
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
period |
float |
Y |
Rate (in seconds) at which to request Status messages. Default: 0.1 |
max_missed_messages |
int |
Y |
Maximum number of heartbeats that may be missed consecutively before a halt is triggered. Default: 3 |
timeout |
float |
Y |
Seconds to wait for response before considering it to be missed. Default: 1.0 |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
none
Example
ethercat_end_status_monitor() # end default status monitor first
ethercat_begin_status_monitor(server_port=xx) # begin monitoring the MotionPlus Service through a different port
ethercat_end_status_monitor
ethercat_end_status_monitor()
Description
Stops any Status Monitor task started with ethercat_begin_status_monitor()
Monitoring will automatically end when a program completes, but this command can be used to stop monitoring at any point in the program
Parameter
none
Return
none
Example
# end default status monitor first
ethercat_end_status_monitor()
# begin monitoring the MotionPlus Service through a different port
ethercat_begin_status_monitor(server_port=xx)
ethercat_clear_error
ethercat_clear_error(timeout = 1.0, server_port = 0, server_host = "")
Description
Clears the EtherCAT Master error
This function allows the user to clear the error on the EtherCAT Master using the ClearError XML-RPC method
This function will halt the program with a popup, if it times out, or if an error is encountered (e.g. if there is a configuration error on the EtherCAT Master)
If the EtherCAT Master halts on a runtime error, and the Status Monitoring is disabled, user will be unable to start the EtherCAT Master again until the error is cleared
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
timeout |
float |
Y |
Seconds to wait for operation to finish before failing with error. Default: 1.0 |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
none
Example
# Status Monitor (started by `ethercat_begin_status_monitor()`)
# has detected an EtherCAT Master runtime error and stopped the previous UR program.
ethercat_clear_error() # Clear current error
# Proceed to reconfigure and restart EtherCAT Master.
ethercat_is_axis_in_fault_state
ethercat_is_axis_in_fault_state(axis_name)
Description
Checks whether the specified axis has reported fault on EtherCAT network
This function requires the EtherCAT Master to be running by calling ethercat_start()
However, if the EtherCAT Master is not running, it will return the last known status
This function will halt the program with an error, if the value of the
axis_name
parameter is invalidThis function requires one axis to be defined
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
The name of the axis. This should match an axis name created by calling |
Return
bool value where True indicates the axis has a fault
Example
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start() # starts up ethercat
# query whether or not "axis1" is in fault state
isAxis1Faulted = ethercat_is_axis_in_fault_state("axis1")
ethercat_get_axis_errors
ethercat_get_axis_errors(timeout = 1.0, server_port = 0, server_host = "")
Description
Gets the axis error codes for all axes on the EtherCAT network
Returns a struct containing a list of drive (axis) indices and a corresponding list of error codes from those drives
If the EtherCAT master is not running, the return value may be invalid
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
timeout |
float |
Y |
Seconds to wait for method result before failing with error. Default: 1.0 |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
struct
AxisErrors type structure containing the following:
timestamp.sec (int) whole seconds elapsed since the UNIX epoch (1970-01-01, 00:00:00) in local time zone
timestamp.nsec (int) nanoseconds elapsed in addition to timestamp.sec
axis_count (int) number of configured axis
axis_indices (int[]) list of axis indices EtherCAT Master is controlling
axis_errors (int[]) list of current error codes for the axes listed in axis_indices
Example
# make sure the ethercat master is stopped and clear the configured ethercat slaves...
# this makes the program start with a clean slate
ethercat_stop(True)
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start() # starts up ethercat
# do stuff
msg = ethercat_get_axis_errors()
textmsg(str_cat("Current error code for axis1: ", to_str(msg.axis_errors[0])))
ethercat_stop(False) # stop the ethercat master only... don't clear the ethercat slaves
ethercat_reset_axis_fault
ethercat_reset_axis_fault(axis_name, timeout = 10)
Description
Resets fault status for the axis specified by axis_name
This function requires the EtherCAT Master to be running
This function blocks until either the fault is successfully reset or the timeout expires
This function returns True if it succeeded and False if it timed out while trying to reset the fault
This function will halt the program with an error, if the value of the
axis_name
parameter is invalidThis function requires one axis to be defined
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
The name of the axis. This should match an axis name created by calling |
timeout |
float |
Y |
Maximum time in seconds to wait for fault to be reset. Negative values indicate unlimited time. Default: 10 |
Return
bool value where True indicates the fault was successfully reset while False indicates it timed out
Example
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start()
if ethercat_is_axis_in_fault_state("axis1"):
ethercat_reset_axis_fault("axis1") # reset fault of "axis1" if there is any
end
ethercat_is_axis_disabled
ethercat_is_axis_disabled(axis_name)
Description
Checks whether the specified EtherCAT axis is disabled
This function requires the EtherCAT Master to be running. If the EtherCAT Master is not running, it will provide the last known status
This function will halt the program with an error, if the value of the
axis_name
parameter is invalidThis function requires one axis to be defined
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
The name of the axis. This should match an axis name created by calling |
Return
bool value where True indicates the axis is disabled
Example
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start()
ethercat_enable_axis("axis1") # enables the axis
# do stuff
ethercat_disable_axis("axis1") # disables the axis
disabled = ethercat_is_axis_disabled("axis1") # would be True if ethercat_disable_axis() succeeded
ethercat_disable_axis
ethercat_disable_axis(axis_name, timeout = 10)
Description
Disables the EtherCAT axis specified by axis_name
This function requires the EtherCAT Master to be running
This function blocks until either the axis is disabled or the timeout expires
This function returns True if it succeeded and False if it timed out
This function will halt the program with an error, if the value of the
axis_name
parameter is invalidThis function requires one axis to be defined
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
The name of the axis. This should match an axis name created by calling |
timeout |
float |
Y |
Maximum time in seconds to wait for axis to be disabled. Negative values indicate unlimited time. Default: 10 |
Return
bool value where True indicates the axis was disabled and False if it timed out
Example
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start()
ethercat_enable_axis("axis1") # enables the axis
# do stuff
ethercat_disable_axis("axis1") # disables the axis
ethercat_is_axis_enabled
ethercat_is_axis_enabled(axis_name)
Description
Checks whether the specified EtherCAT axis is enabled
This function requires the EtherCAT Master to be running. If the EtherCAT Master is not running, it will provide the last known status
This function will halt the program with an error, if the value of the
axis_name
parameter is invalidThis function requires one axis to be defined
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
The name of the axis. This should match an axis name created by calling |
Return
bool where True indicates that the axis is enabled
Example
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start()
ethercat_enable_axis("axis1") # enables the axis and waits 1s to verify the axis remains enabled before continuing
enabled = ethercat_is_axis_enabled("axis1") # would be True if ethercat_enable_axis() succeeded
ethercat_enable_axis
ethercat_enable_axis(axis_name, timeout = 10, steady_time = 0.5)
Description
Enables the EtherCAT axis specified by axis_name
This function requires the EtherCAT Master to be running
This function blocks until either the axis is enabled or the timeout expires
This function returns True if it succeeded and False if it timed out
This function will halt the program with an error, if the value of the
axis_name
parameter is invalidThis function requires one axis to be defined
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
The name of the axis. This should match an axis name created by calling |
timeout |
float |
Y |
Maximum time in seconds to wait for axis to be enabled. Negative values indicate unlimited time. Default: 10 |
steady_time |
float |
Y |
Time to wait after enabling axis to verify that it stays in operation enabled state. Default: 0.5 |
Return
bool value where True indicates the axis was enabled and False if it timed out
Example
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start()
ethercat_enable_axis("axis1") # enables the axis and waits 1s to verify the axis remains enabled before continuing
ethercat_get_axis_positions
ethercat_get_axis_positions(timeout = 1.0, server_port = 0, server_host = "")
Description
Gets the last known axis positions for all axes on the EtherCAT network
This function returns a struct with a timestamp, axis indices, target axis positions, actual axis positions, target axis position encoder counts and actual axis position encoder counts
This function requires the EtherCAT Master to be running for the returned data to be valid
This function will halt the program with an erorr, if the EtherCAT Master is not running
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
timeout |
float |
Y |
Seconds to wait for method result before failing with error. Default: 1.0 |
server_port |
int |
Y |
TCP port for the XML-RPC server. Defaults to |
server_host |
string |
Y |
Hostname / IP address for the XML-RPC server. Defaults to “”, implying that |
Return
struct
structure containing the following:
timestamp.sec (int) whole seconds elapsed since the UNIX epoch (1970-01-01, 00:00:00) in local time zone
timestamp.nsec (int) nanoseconds elapsed in addition to timestamp.sec
axis_count (int) number of configured axis
axis_indices (int[]) list of axis indices EtherCAT Master is controlling
target_positions (float[]) list of target positions in metric units for the axes listed in axis_indices
actual_positions (float[]) list of current actual positions in metric units for the axes listed in axis_indices
target_position_counts (int[]) list of target positions in encoder counts for the axes listed in axis_indices
actual_position_counts (int[]) list of actual positions in encoder counts for the axes listed in axis_indices
Example
ethercat_stop(True) # make sure the ethercat master is stopped and clear the configured ethercat slaves... this makes the program start with a clean slate
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start()
# do stuff
positions = ethercat_get_axis_positions()
textmsg(str_cat("Current actual encoder counts for axis1: ", to_str(positions.actual_position_counts[0])))
ethercat_stop(False) # stop the ethercat master only... don't clear the ethercat slaves
ethercat_home_axis_direct
ethercat_home_axis_direct(axis_name, timeout = 1.0)
Description
: performs EtherCAT homing method: Home to current.
This function returns a boolean flag indicating whether the function exited due to timeout.
This function requires the EtherCAT Master to be running for it to succeed
This function requires the axis to be enabled
Parameter
Name |
Type |
Optional |
Description |
---|---|---|---|
axis_name |
string |
N |
The name of the axis. This should match an axis name created by calling |
timeout |
float |
Y |
Seconds to wait for method result before failing with error. Default: 10.0 |
Return
bool value where True indicates the axis was homed and False if it timed out
Example
ethercat_stop(True) # make sure the ethercat master is stopped and clear the configured ethercat slaves... this makes the program start with a clean slate
axis_group_add("groupA", tf, "world")
axis_group_add_axis("groupA", "axis1", "", p[0,0,0,0,0,0], AXIS_TYPE, VELOCITY_LIMIT, ACCLERATION_LIMIT)
ethercat_config_axis("axis1", 1, ENCODER_RESOLUTION, GEAR_RATIO, FEED_CONSTANT, 0)
ethercat_start() # starts up ethercat
# do stuff
positions = axis_group_get_target_positions("groupA")
textmsg(str_cat("Axis 1 position: ", to_str(positions[0])))
ethercat_home_axis_direct("axis1")
positions = axis_group_get_target_positions("groupA")
textmsg(str_cat("Axis 1 position: ", to_str(positions[0]))) # axis has been homed and position is now 0.0
ethercat_stop(False) # stop the ethercat master only... don't clear the ethercat slaves
Miscellaneous utility functions
time_sec()
Description: returns the current execution time of the UR Controller in seconds
Parameter: none
Return
float current execution time in seconds
Example
cur_time = time_sec()
textmsg(str_cat("Current time in seconds: ", to_str(cur_time)))
Calling EtherCAT Functions In Third Party URCaps
The EtherCAT functions provided by the MotionPlus URCap can be used by third party URCaps, however, there are some known limitations and workarounds that third party developers should be aware of:
Calling EtherCAT Functions from a URCap’s Installation Node Preamble Contribution:
PolyScope does not guarantee the order of URCap installation node contributions to the URScript preamble. This presents a problem in the scenario where a third party URCap has a contribution that calls functions from the MotionPlus EtherCAT contribution, but Polyscope may have placed the MotionPlus contribution after the third party URCap call which would result in an error during execution
The workaround is for the third party URCap installation node’s generateScript()
method to check if Polyscope has already added the EtherCAT API to the program and add it to the preamble if it hasn’t been. To accomplish this, 3rd party developers can find the EtherCAT API implementation placed in /usr/local/motionplus/ethercat_definitions.script. The ethercat_definitions.script file contains a UUID that looks like this: # UUID: UR_ETHER_09823745092847509284753
. The third party installation node can find the UUID in the preamble generated by Polyscope and add it if it’s not there. Here’s an example J ava implementation of an installation node generateScript() method that accomplishes this :
@Override
public void generateScript(ScriptWriter writer) {
String line = null;
try {
String preambleFileContents = new String(
Files.readAllBytes(Paths.get("/usr/local/motionplus/ethercat_definitions.script")));
boolean ethercatPreambleAlreadyIncludedByPolyscope = false;
// Look for the UUID signature in the preamble file.
final Pattern pattern = Pattern.compile("# UUID:\\s+([A-Za-z0-9]+(_[A-Za-z0-9]+)+)", Pattern.CASE_INSENSITIVE);
final Matcher matcher = pattern.matcher(preambleFileContents);
// If the UUID was found then, look for it in the preamble created by Polyscope
// so far to see if it has already been included.
if (matcher.find()) {
final String currentPolyscopePreamble = writer.generateScript();
final String foundUUIDString = preambleFileContents.substring(matcher.start(), matcher.end());
// See if the UUID string is already in the Polyscope preamble, if so, then we
// assume that the motionplus preamble doesn't need to be added again.
ethercatPreambleAlreadyIncludedByPolyscope = currentPolyscopePreamble.contains(foundUUIDString);
}
// If the UUID string hasn't been found, then add the MotionPlus preamble to the
// Polyscope preamble line by line to ensure proper URScript line formatting.
if (!ethercatPreambleAlreadyIncludedByPolyscope) {
BufferedReader bufReader = new BufferedReader(new StringReader(preambleFileContents));
while ((line = bufReader.readLine()) != null) {
writer.appendLine(line);
}
}
// The 3rd party contribution can be added here now that the EtherCAT API
// has been placed in the URScript file
} catch (IOException e) {
e.printStackTrace();
}
}
The following imports will need to be added to the top of the java implementation file:
import com.ur.urcap.api.domain.script.ScriptWriter
import java.io.BufferedReader;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
Note: The MotionPlus Installation Node looks to see if the EtherCAT API has already been added to the preamble by a third party URCap. If it’s already there, then it’s not added again.
Calling EtherCAT Functions in Primary/Secondary Interface Program
The EtherCAT API is not included in any programs run over the primary or secondary interfaces. To call EtherCAT API methods in primary/secondary interface programs, they must include the API before they are called in the program. This can be accomplished by prepending the contents of the EtherCAT API found in /usr/local/motionplus/ethercat_definitions.script
to the URScript program.
Here is example Java code that sends a primary program to the controller that starts the EtherCAT master if it isn’t already started, enables an axis called “axis1”, and then presents the user with a popup that displays if the axis is enabled.
public void runPrimaryProgram() {
String primaryProgram = null;
// Construct the primary program to be sent to the controller
try {
// Grab the EtherCAT API definitions from the filesystem
String preambleFileContents = new String(
Files.readAllBytes(Paths.get("/usr/local/motionplus/ethercat_definitions.script")));
StringBuilder programBuilder = new StringBuilder();
programBuilder.append("def myPrimaryProgram():\n"); // primary programs are placed inside a URScript function, the
// name doesn't matter
// Write the EtherCAT API preamble line by line into the program with whitespace
// to maintain proper indentation. The controller reads an unindented "end" as
// the end of the program, so it's important that propper indentation of the
// EtherCAT preamble and third party code is maintained. (It also improves
// readability of logfiles.)
BufferedReader bufReader = new BufferedReader(new StringReader(preambleFileContents));
String line = null;
while ((line = bufReader.readLine()) != null) {
programBuilder.append(" " + line + "\n");
}
////// Start third party code here ////////
programBuilder.append(" if (not ethercat_is_master_running()):\n");
programBuilder.append(" ethercat_start()\n");
programBuilder.append(" end\n");
programBuilder.append(" ethercat_enable_axis(\"axis1\")\n");
programBuilder.append(" popup(ethercat_is_axis_enabled(\"axis1\"))\n");
////// End third party code here ////////
programBuilder.append("end\n"); // the unindented "end" marks the end of the primary program
primaryProgram = programBuilder.toString();
} catch (IOException e) {
System.err.println("Failed to construct the primary program.");
return;
}
// Send the program to the controller
try {
Socket socket = new Socket("127.0.0.1", 30001); // primary interface is port 30001
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.write(primaryProgram.getBytes("US-ASCII"));
outputStream.flush();
socket.close();
} catch (IOException e) {
System.err.println("Failed to write the primary program to the socket.");
}
}
The following imports will need to be added to the top of the java implementation file:
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.DataOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.net.Socket;
Note: The controller only runs URScript primary programs when the robot is powered on. Also, if this code is being run as part of a URCap GUI (e.g., called when pressing a button), then it should be run by a Java thread to ensure that the GUI stays responsive as in the example below:
public void runPrimaryProgramFromThread() {
new Thread(new Runnable() {
@Override
public void run() {
runPrimaryProgram();
}
}).start();
}