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 0, implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 key.

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 0, implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 to axis_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 -1, implying that it will be determined automatically from axis name.

timeout

float

Y

Timeout in seconds to wait for the operation to finish. Defaults to 2.

server_port

int

Y

TCP port for the XML-RPC server. Defaults to 0 , implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 0, implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 0, implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 0, implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 0, implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 0 , implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 invalid

  • This 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 axis_group_add_axis().

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 0 , implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 invalid

  • This 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 axis_group_add_axis().

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 invalid

  • This 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 axis_group_add_axis().

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 invalid

  • This 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 axis_group_add_axis().

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 invalid

  • This 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 axis_group_add_axis().

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 invalid

  • This 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 axis_group_add_axis().

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 0 , implying that the default server port will be used.

server_host

string

Y

Hostname / IP address for the XML-RPC server. Defaults to “”, implying that localhost will be used.

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 axis_group_add_axis().

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();
  }