Urcap Tutorial Swing
Urcap Tutorial Swing
Abstract
URCaps make it possible to seamlessly extend any Universal Robot with customized func-
tionality. Using the URCap Software Platform, a URCap developer can define customized
installation screens and program nodes for the end user. These can, for example, encapsu-
late new complex robot programming concepts, or provide friendly hardware configuration
interfaces.
This tutorial explains how to use the URCap Software Platform version 1.14.0 to develop
and deploy URCaps with Swing-based user interfaces for PolyScope version 5.15.0 running
on e-Series robots. Older PolyScope versions can be targeted using older versions of the
URCap Software Platform. URCaps can be developed for CB3 robots with PolyScope
version 3.3.x to 3.15.x using version 1.12.0 or older of the URCap Software Platform.
Contents
1 Introduction 3
1.1 Features in URCap Software Platform 1.14.0 . . . . . . . . . . . . . . . . . . . . 3
2 Prerequisites 5
3 URCap SDK 6
9 Contribution of a Daemon 33
9.1 Daemon Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
9.2 Interaction with the Daemon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
9.3 C/C++ Daemon Executables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
9.4 Tying the different Contributions together . . . . . . . . . . . . . . . . . . . . . . 37
12 Compatibility 53
12.1 Robot Series Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
12.1.1 Compatibility Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
12.1.2 Installation and Startup of URCaps . . . . . . . . . . . . . . . . . . . . . 54
12.1.3 Updating Existing URCaps . . . . . . . . . . . . . . . . . . . . . . . . . . 54
12.2 API Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
12.2.1 Advanced compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
13 Exception Handling 58
14 Troubleshooting 59
1 Introduction
The first official version of the URCap Software Platform (version 1.0.0) was released with
PolyScope version 3.3.0 running on CB3 robots. This tutorial describes features supported in
version 1.14.0 of the URCap Software Platform which is released together with PolyScope ver-
sion 5.15.0 for e-Series robots. To create URCaps for PolyScope version 3.3.x to 3.15.x running
on CB3 robots, older versions of the URCap Software Platform must be used.
This platform can be used to develop external contributions to PolyScope that are loaded when
PolyScope starts up. This makes it possible for a URCap developer to provide customized func-
tionality within PolyScope.
For example, a customized installation screen can serve the end user to comfortably configure a
new tool. Similarly, a customized program node may serve as a way to perform complex tasks
while hiding unnecessary detail.
The layout of a customized screen, the behaviour of a customized node, data persistence and
script code generation is all implemented in Java. The URCap along with its resources is pack-
aged and distributed as a single Java Jar file with the .urcap file extension. A URCap can be
installed from the URCap Settings/Setup screen in PolyScope.
• Saving and loading of data underlying the customized installation node as part of the
currently loaded installation in PolyScope.
• Script code that an installation node contributes to the preamble of a robot program.
2 Prerequisites
A working version of Java SDK 6 is required for URCap development along with Apache Maven
3.0.5. You will also need PolyScope version 5.15.0 in order to install the developed URCap, if it
is using URCap API version 1.14.0. Previous versions of the API will have lower requirements
for the PolyScope version. Any Universal Robots robot (e.g., a UR3, UR5, UR10, etc.) can be
used for that purpose or the Universal Robots offline simulator software (URSim). PolyScope
and the offline simulator can be found in the download area of the tech support website at:
www.universal-robots.com/support
Select the applicable version and follow the given installation instructions. The offline simulator
is available for Linux and non-Linux operating systems through a virtual Linux machine.
The script language and pre-defined script functions are defined in the script manual, which can
also be found in the download area of the tech support website.
The URCap SDK is freely available on the Universal Robots+ website at:
https://plus.universal-robots.com
The following section 3. URCap SDK describes the content of the URCap SDK.
3 URCap SDK
The URCap SDK provides the basics to create a URCap. It contains a Java package with
the API that the developer will program against, documentation, the Hello World Swing and
other URCap examples, the urtool3 cross-compiler toolchain and means of easily creating a new
empty Maven-based template URCap project (See section 11. Creating new thin Projects using
a Maven Archetype) as well as easily updating an existing URCap.
com.ur.urcap.sdk
artifacts
api
.
.
.
1.14.0
com.ur.urcap.api-1.14.0.jar
com.ur.urcap.api-1.14.0-javadoc.jar
com.ur.urcap.api-1.14.0-sources.jar
archetype
com.ur.urcap.archetype-1.14.0.jar
other
commons-httpclient-3.1.0.0.jar
ws-commons-util-1.0.2.0.jar
The URCap SDK is distributed as a single ZIP file. Figure 1 shows the structure of the file.
A description of the directories and files contained in this file is given below:
/artifacts/: This directory holds all released versions of the URCap API in separate folders, the
Maven archetype and other necessary files. Each folder named with a version number holds
the Java packages, (e.g. com.ur.urcap.api-1.14.0*.jar files), that contains Java interfaces,
Javadoc and sources of the URCap API that are necessary to implement the Java portion
of a URCap. The Maven archetype folder holds the com.ur.urcap.archetype-1.14.0.jar
file, that can be used to create a new empty template URCap project.
/doc/: The directory contains this tutorial (both in a HTML-based version and a Swing-based
version) as well as a document describing how to configure child program nodes in a sub-
tree and a document explaining how to work with variables. Included is also a document
with a guide on how to convert an existing URCap with HTML-based user interface to a
Swing-based one.
/samples/: A folder containing example projects demonstrating different features of the soft-
ware framework. A description of the examples is found in section 10. URCap Examples
Overview.
/urtool/: Contains the urtool3 cross-compiler toolchain that should be used when building
C/C++ daemon executables for the CB3.0/3.1 and CB5.0 control boxes.
upgradeURCap.sh: A script which can be used to update an existing URCap (project) to spec-
ify which robot series the URCap is compatible with (see section 12.1. Robot Series
Compatibility).
readme.txt: A readme file describing the content of the SDK.
5. Select a .urcap file, e.g. helloworldswing-1.0-SNAPSHOT.urcap and tap the Open button.
6. Restart PolyScope using the Restart button in the bottom of the screen:
CB3 robot
CB3 robot
Furthermore, the Hello World program node is available in the Program tab. On CB3 robots,
the node is visible within the Structure tab after selecting the URCaps subtab:
CB3 robot
On e-Series robots, the node is visible on the left of the screen after tapping URCaps:
CB3 robot
com.ur.urcap.examples.helloworldswing
pom.xml
assembly.xml
src
main
java
com
ur
urcap
examples
helloworldswing
impl
Activator.java
HelloWorldInstallationNodeContribution.java
HelloWorldInstallationNodeService.java
HelloWorldInstallationNodeView.java
Figure 2 shows the structure of the Hello World Swing URCap project. This project consists of
the following parts:
1. A Java view part consisting of two screens with the layout specified in the two files
HelloWorldProgramNodeView.java and HelloWorldInstallationNodeView.java.
3. A license META-INF/LICENSE with the license information that is shown to the user when
the URCap is installed.
4. Maven configuration files pom.xml and assembly.xml for building the project.
The My Daemon Swing URCap is an extended version of the Hello World Swing URCap, that
exemplifies the integration of an external daemon process. Figure 3 shows the structure of the
My Daemon Swing URCap project. Compared to the Hello World Swing project it additionally
offers the following parts:
The Python and C++ daemons are alternatives that provide the same functionality.
com.ur.urcap.examples.mydaemonswing
pom.xml
assembly.xml
daemon
.
.
.(See Figure 6, page 37)
src
main
java
com
ur
urcap
examples
mydaemonswing
The services:
• HelloWorldInstallationNodeService.java
• HelloWorldProgramNodeService.java
are registered in Activator.java and thereby a new installation node and program node are of-
fered to PolyScope. The My Daemon Swing additionally registers its MyDaemonDaemonService.java
service to make the daemon executable available to PolyScope.
The file pom.xml contains a subsection (under the <properties> section) with a set of properties
for the URCap with meta-data specifying the vendor, contact address, copyright, description,
and short license information which will be displayed to the user when the URCap is installed
in PolyScope. See Listing 1 for the Hello World Swing version of these properties.
Listing 1: Section with meta-data properties inside the pom.xml file for the Hello World Swing
URCap
1 <! - -******************************************************************** - - >
2 <! - - Note : Update this section with relevant meta data -->
3 <! - - that comes along with your URCap -->
4 <! - -******************************************************************** - - >
5 <! - -******************* BEGINNING OF URCAP META DATA ******************* - - >
6 < urcap . symbolicname > com . ur . urcap . examples . helloworldswing </ urcap . symbolicname >
7 < urcap . vendor > Universal Robots </ urcap . vendor >
The URCap meta-data subsection also contains two boolean compatibility flag properties (urcap.
compatibility.CB3 and urcap.compatibility.eSeries) which specify the URCap’s compatibility with
the CB3 and the e-Series robot series, respectively. See section 12.1. Robot Series Compatibility
for more information about these compatibility flags.
Deployment to a robot with Maven Given the IP address of the robot, e.g. 10.2.128.64,
go to your URCap project folder and type:
1 $ cd samples / swing / com . ur . urcap . examples . he l lo wo rl d sw in g
2 $ mvn install - Premote - Durcap . install . host =10.2.128.64
and the URCap is deployed and installed on the robot. During this process PolyScope will be
restarted.
You can also specify the IP address of the robot via the property urcap.install.host inside the
pom.xml file. Then you can deploy by typing:
Deployment to URSim If you are running Linux then URSim can be installed locally.
Otherwise it needs to run in a virtual machine (VM). It is possible to deploy to both environments
with Maven. As shown above parameters can be supplied either directly on the command line
• To deploy to a locally running URSim specify the path to the extracted URSim with the
property ursim.home.
• To deploy to a URSim running in a VM specify the IP address of the VM using the
property ursimvm.install.host.
Once the properties are configured you can deploy to a local URSim by using the ursim profile:
1 $ cd samples / swing / com . ur . urcap . examples . he l lo wo rl d sw in g
2 $ mvn install -P ursim
Note, if you are using VirtualBox to run the VM you should make sure that the network of the
VM is operating in bridged mode.
Listing 2: The view (UI) of the customized Hello World installation screen
1 package com . ur . urcap . examples . h e ll ow or l ds wi ng . impl ;
2
3 import com . ur . urcap . api . contribution . installation . swing .
SwingInstallationNodeView ;
4 import com . ur . urcap . api . domain . u s er in t er ac ti o n . keyboard . K e y b o a r d T e x t I n p u t ;
5
6 import javax . swing . BorderFactory ;
7 import javax . swing . Box ;
8 import javax . swing . BoxLayout ;
9 import javax . swing . JLabel ;
47 pane . setText ( " The popup title below is shared between all Hello World
program nodes .\ nThe title cannot be empty . " ) ;
48 pane . setEditable ( false ) ;
49 pane . se tMaximum Size ( pane . g e t P r e f e r r e d S i z e () ) ;
50 pane . setBackground ( infoBox . getBackground () ) ;
51 infoBox . add ( pane ) ;
52 return infoBox ;
53 }
54
55 private Box createInput ( final H e l l o W o r l d I n s t a l l a t i o n N o d e C o n t r i b u t i o n
i n s t a l l a t i o n N od e ) {
56 Box inputBox = Box . c r e a t e H o r i z o n t a l B o x () ;
57 inputBox . setAlignmentX ( Component . LEFT_A LIGNMEN T ) ;
58
59 inputBox . add ( new JLabel ( " Popup title : " ) ) ;
60 inputBox . add ( c r e a t e H o r i z o n t a l S p a c i n g () ) ;
61
62 jTextField = new JTextField () ;
63 jTextField . setFocusable ( false ) ;
64 jTextField . s e t P r e f e rr e d S i z e ( style . g e t I n p u t f i e l d S i z e () ) ;
65 jTextField . setMa ximumSi ze ( jTextField . g e t P r e f e r r e d S i z e () ) ;
66 jTextField . a d d M o u s e Li s t e n e r ( new MouseAdapter () {
67 @Override
68 public void mousePressed ( MouseEvent e ) {
69 K e y b o a r d T e x t I n p u t keyboardInput = i n s t a l l a t i o nN o d e .
g e t I n p u t F o r T e x t F i e l d () ;
70 keyboardInput . show ( jTextField , i n s t a l l a t i o n N o d e .
g e t C a l l b a c k F o r T e x t F i e l d () ) ;
Listing 2 shows the content of the HelloWorldInstallationNodeView.java file which defines the
layout of the screen used for the Hello World installation node. The class uses various Swing
GUI components to construct the user interface. These components are added to the JPanel
provided as argument in the buildUI(JPanel, InstallationNodeContribution) method.
The panel has a fixed size that cannot be changed. Margins are already added by PolyScope,
so the entire area of the panel can be used for UI components. The title will also be set au-
tomatically to the value returned by SwingInstallationNodeService.getTitle(Locale) (described in
section 7.2. Making the customized Installation Node available to PolyScope). It will have a
Swing UI manager already set, meaning that components without additional styling will look
as native PolyScope ones including the correct font types. In order to resemble PolyScope the
components should therefore only use limited styling, such as font sizes or input field sizes (if
the applied ones are not suitable). Calling methods not supported by PolyScope will result in
The corresponding installation node contribution is passed as the second argument to the method
buildUI(JPanel, InstallationNodeContribution) to enable the view and the contribution to commu-
nicate with each other in order to pass values and react to events.
This structure creates a model-view separation where the view is created in the aforementioned
class and the model is handled in the contribution. The corresponding Java code is presented
in the following two sections.
The SwingInstallationNodeService interface requires two type variables for the specific implemen-
tations of the InstallationNodeContribution and SwingInstallationNodeView interface, respectively,
as well as the following methods to be defined:
• getTitle(Locale) returns the title for the node, to be shown on the left side of the Installation
tab to access the customized installation screen. For simplicity, the title is specified simply
as "Hello World". In a more realistic example, the return value of the getTitle(Locale)
method would be translated into the language specified by standard Java localization,
based on the provided Locale argument. The title is also used as the fixed header for the
installation node screen in PolyScope. The method is only called once.
• createView(ViewAPIProvider) returns an instance of the view (described in section 7.1. UI
of the Installation Node View). Use the ViewAPIProvider to access information about the
software version, language settings (can be used to provide a translated UI), etc.
• createInstallationNode(InstallationAPIProvider, SwingInstallationNodeView, DataModel,
CreationContext) is called by PolyScope when it needs to create an instance of the installa-
tion node. The arguments are:
– InstallationAPIProvider: provides access to various PolyScope domain APIs relevant
for an installation node
– SwingInstallationNodeView: the view instance created by the createView(ViewAPIProvider)
described above
Listing 4: Java class defining functionality for the Hello World installation node
1 package com . ur . urcap . examples . h e ll ow or l ds wi ng . impl ;
2
3 import com . ur . urcap . api . contribution . I n s t a l l a t i o n N o d e C o n t r i b u t i o n ;
4 import com . ur . urcap . api . contribution . installation . I n s t a l l a t i o n A P I P r o v i d e r ;
5 import com . ur . urcap . api . domain . data . DataModel ;
6 import com . ur . urcap . api . domain . script . ScriptWriter ;
7 import com . ur . urcap . api . domain . u s er in t er ac ti o n . keyboard . K e y b o a r d I n p u t C a l l b a c k ;
8 import com . ur . urcap . api . domain . u s er in t er ac ti o n . keyboard . K e y b o a r d I n p u t F a c t o r y ;
9 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d T e x t I n p u t ;
10
11 public class H e l l o W o r l d I n s t a l l a t i o n N o d e C o n t r i b u t i o n implements
InstallationNodeContribution {
12
13 private static final String P OPUPTITL E_KEY = " popuptitle " ;
14 private static final String DEFAULT_VALUE = " Hello World " ;
15 private final H e l l o W o r l d I n s t a l l a t i o n N o d e V i e w view ;
16 private final K e y b o a r d I n p u t F a c t o r y k ey bo a rd Fa ct o ry ;
17
18 private DataModel model ;
19
20 public H e l l o W o r l d I n s t a l l a t i o n N o d e C o n t r i b u t i o n ( I n s t a l l a t i o n A P I P r o v i d e r
apiProvider , DataModel model , H e l l o W o r l d I n s t a l l a t i o n N o d e V i e w view ) {
21 this . ke yb o ar dF ac t or y = apiProvider . g e t U s e r I n t e r f a c e A P I () .
g e t U s e r I n t e r a c t i o n () . g e t K e y b o a r d I n p u t F a c t o r y () ;
22 this . model = model ;
23 this . view = view ;
61 }
62
63 public K e y b o a r d T e x t I n p u t g e t I n p u t F o r T e x t F i e l d () {
64 K e y b o a r d T e x t I n p u t keyboardInput = k e yb oa r dF ac to r y .
c r e a t e S t r i n g K e y b o a r d I n p u t () ;
65 keyboardInput . s et In it i al Va lu e ( getPopupTitle () ) ;
66 return keyboardInput ;
67 }
68
69 public KeyboardInputCallback < String > g e t C a l l b a c k F o r T e x t F i e l d () {
70 return new KeyboardInputCallback < String >() {
71 @Override
72 public void onOk ( String value ) {
73 setPopupTitle ( value ) ;
74 view . setPopupText ( value ) ;
75 }
76 };
77 }
78 }
The data model which was mentioned in section 7.2. Making the customized Installation Node
available to PolyScope is passed into the constructor through a DataModel object. All data that
needs to be saved and loaded along with a robot installation must be stored in and retrieved
from this model object.
When the user interacts with the text input field defined in the view, the listener defined there
The openView() method is called whenever the user enters the screen. It sets the contents of
the text input field defined in the view to the value stored in the data model. The closeView()
method is called when the user leaves the screen.
Finally, the preamble of each program run with this URCap installed will contain an assignment
in its preamble, as specified in the implementation of the generateScript(ScriptWriter) method.
In the assignment, the script variable named "hello_world_swing_popup_title" is assigned to a
string that contains the popup title stored within the data model object.
This means that Java garbage collection has a chance to clean up previous instances in case
any listeners have been forgotten or other potential memory leaks have been created. This also
means that no references to the view instance or contribution instance should be kept outside
these classes. Respecting this will protect PolyScope from running out of memory.
Listing 5 shows the definition of the layout of a simple program node. It contains a single input
text field where the user can type a name and two labels that provide a preview of the popup
that will be displayed at runtime. A label defined in the view will be used to display the title
set in the installation. The name that is entered by the end user in the Hello World program
node will be used to construct a customized popup message. This message will also be shown
in the preview label with name "previewMessage".
The linking of the view and the program node contributions is presented in the following section.
Listing 5: The view (UI) of the customized Hello World program screen
1 package com . ur . urcap . examples . h e ll ow or l ds wi ng . impl ;
2
3 import com . ur . urcap . api . contribution . C o n t r i b u t i o n P r o v i d e r ;
4 import com . ur . urcap . api . contribution . program . swing . S w i n g P r o g r a m N o d e V i e w ;
5 import com . ur . urcap . api . domain . us er in t er ac ti o n . keyboard . K e y b o a r d T e x t I n p u t ;
6
7 import javax . swing . Box ;
8 import javax . swing . BoxLayout ;
9 import javax . swing . JLabel ;
10 import javax . swing . JPanel ;
11 import javax . swing . JTextField ;
12 import java . awt . Component ;
13 import java . awt . Dimension ;
14 import java . awt . Font ;
15 import java . awt . event . MouseAdapter ;
16 import java . awt . event . MouseEvent ;
17
18 public class H e l l o W o r l d P r o g r a m N o d e V i e w implements SwingProgramNodeView <
H e l l o W o r l d P r o g r a m N o d e C o n t r i b u t i o n >{
19
20 private final Style style ;
21 private JTextField jTextField ;
22 private JLabel previewTitle ;
23 private JLabel prev iewMessa ge ;
24
25 public H e l l o W o r l d P r o g r a m N o d e V i e w ( Style style ) {
26 this . style = style ;
27 }
28
29 @Override
30 public void buildUI ( JPanel jPanel , final ContributionProvider <
H e l l o W o r l d P r o g r a m N o d e C o n t r i b u t i o n > provider ) {
31 jPanel . setLayout ( new BoxLayout ( jPanel , BoxLayout . Y_AXIS ) ) ;
32
33 jPanel . add ( createInfo () ) ;
34 jPanel . add ( c r e a t e V e r t i c a l S p a c i n g ( style . g e t V e r t i c a l S p a c i n g () ) ) ;
35 jPanel . add ( createInput ( provider ) ) ;
36 jPanel . add ( c r e a t e V e r t i c a l S p a c i n g ( style . g e t E x t r a L a r g e V e r t i c a l S p a c i n g () ) ) ;
37 jPanel . add ( createPreview () ) ;
38 }
39
40 private Box createInfo () {
41 Box infoBox = Box . c r e a t e H o r i z o n t a l B o x () ;
42 infoBox . setAlignmentX ( Component . LEF T_ALIGNM ENT ) ;
43 infoBox . add ( new JLabel ( " This program node will open a popup on execution . "
));
44 return infoBox ;
45 }
46
47 private Box createInput ( final ContributionProvider <
H e l l o W o r l d P r o g r a m N o d e C o n t r i b u t i o n > provider ) {
48 Box inputBox = Box . c r e a t e H o r i z o n t a l B o x () ;
49 inputBox . setAlignmentX ( Component . LEFT_A LIGNMEN T ) ;
50 inputBox . add ( new JLabel ( " Enter your name : " ) ) ;
51 inputBox . add ( c r e a t e H o r i z o n t a l S p a c i n g () ) ;
52
In order for the view to call methods on the currently selected program node’s underlying con-
tribution, the supplied provider must be used. The get() method on the ContributionProvider
will automatically return the ProgramNodeContribution instance representing the currently selected
program node and in turn its associated data model.
The contribution object on the other hand was instantiated with the one and only view instance
and can call methods on this instance without any further ado.
The getId() method returns the unique identifier for this type of program node. The identifier
will be used when storing programs that contain these program nodes. This method is called
once. Do not change the return value of this method in released URCaps, since it will break
backwards compatibility for existing programs. URCap program nodes in such existing pro-
grams will not be loaded properly and the program can not run anymore.
Its getTitle(Locale) method supplies the text for the button in the Structure Tab that corre-
sponds to this type of program node. It is also used as the title on the Command tab screen
for such nodes. Use the provided Locale if translated titles should be supported. This method
is called once.
The constructor used in the implementation of the createNode(...) method is discussed in sec-
tion 8.4. Functionality of the Program Node. The createNode(...) method call during program
load is discussed in section 8.6. Loading Programs with Program Node Contributions.
Listing 6: Java class defining how Hello World program nodes are created
1 package com . ur . urcap . examples . h e ll ow or l ds wi ng . impl ;
2
3 import com . ur . urcap . api . contribution . Vi e wA PI Pr o vi de r ;
4 import com . ur . urcap . api . contribution . program . C o n t r i b u t i o n C o n f i g u r a t i o n ;
5 import com . ur . urcap . api . contribution . program . C r ea ti o nC on te x t ;
6 import com . ur . urcap . api . contribution . program . P r o g r a m A P I P r o v i d e r ;
7 import com . ur . urcap . api . contribution . program . swing . S w i n g P r o g r a m N o d e S e r v i c e ;
8 import com . ur . urcap . api . domain . SystemAPI ;
9 import com . ur . urcap . api . domain . data . DataModel ;
10
11 import java . util . Locale ;
12
13 public class H e l l o W o r l d P r o g r a m N o d e S e r v i c e implements SwingProgramNodeService <
H e l l o W o r l d P r o g r a m N o d e C o n t r i b u t i o n , HelloWorldProgramNodeView > {
14
15 @Override
16 public String getId () {
17 return " H e l l o W o r l d S w i n g N o d e " ;
18 }
19
20 @Override
21 public void c o n f i g u r e C o n t r i b u t i o n ( C o n t r i b u t i o n C o n f i g u r a t i o n configuration ) {
22 configuration . s e t C h i l d r e n A l l o w e d ( true ) ;
23 }
24
25 @Override
Listing 7: Java class defining functionality for the Hello World program node
1 package com . ur . urcap . examples . h e ll ow or l ds wi ng . impl ;
2
3 import com . ur . urcap . api . contribution . P r o g r a m N o d e C o n t r i b u t i o n ;
4 import com . ur . urcap . api . contribution . program . P r o g r a m A P I P r o v i d e r ;
5 import com . ur . urcap . api . domain . ProgramAPI ;
6 import com . ur . urcap . api . domain . data . DataModel ;
7 import com . ur . urcap . api . domain . script . ScriptWriter ;
8 import com . ur . urcap . api . domain . undoredo . U n do Re do M an ag er ;
9 import com . ur . urcap . api . domain . undoredo . U n do ab le C ha ng es ;
10 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d I n p u t C a l l b a c k ;
11 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d I n p u t F a c t o r y ;
12 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d T e x t I n p u t ;
13
14 public class H e l l o W o r l d P r o g r a m N o d e C o n t r i b u t i o n implements
ProgramNodeContribution {
15 private static final String NAME = " name " ;
16
17 private final ProgramAPI programAPI ;
18 private final U nd o Re do Ma n ag er u nd oR ed o Ma na ge r ;
19 private final K e y b o a r d I n p u t F a c t o r y k ey bo a rd Fa ct o ry ;
20
21 private final H e l l o W o r l d P r o g r a m N o d e V i e w view ;
22 private final DataModel model ;
23
24 public H e l l o W o r l d P r o g r a m N o d e C o n t r i b u t i o n ( P r o g r a m A P I P r o v i d e r apiProvider ,
H e l l o W o r l d P r o g r a m N o d e V i e w view , DataModel model ) {
61 K e y b o a r d T e x t I n p u t keyboardInput = k e yb oa r dF ac to r y .
c r e a t e S t r i n g K e y b o a r d I n p u t () ;
62 keyboardInput . s et In it i al Va lu e ( getName () ) ;
63 return keyboardInput ;
64 }
65
66 public KeyboardInputCallback < String > g e t C a l l b a c k F o r T e x t F i e l d () {
67 return new KeyboardInputCallback < String >() {
68 @Override
69 public void onOk ( String value ) {
70 setPopupTitle ( value ) ;
71 view . setPopupText ( value ) ;
72 }
73 };
74 }
75
76 public void setPopupTitle ( final String value ) {
77 u nd oR ed o Ma na g er . recordChanges ( new Un do a bl eC ha n ge s () {
78 @Override
79 public void ex ecuteCha nges () {
80 if ( " " . equals ( value ) ) {
81 model . remove ( NAME ) ;
82 } else {
83 model . set ( NAME , value ) ;
84 }
85 }
86 }) ;
87
The openView() and closeView() methods specify what happens when the user selects and unse-
lects the underlying program node in the program tree.
The getTitle() method defines the text which is displayed in the program tree for the node. The
text of the node in the program tree is updated when values are written to the DataModel.
The isDefined() method serves to identify whether the node is completely defined (green) or
still undefined (yellow). Note that a node, which can contain other program nodes (see sec-
tion 8.3. Making the customized Program Nodes available to PolyScope), remains undefined as
long as it has a child that is undefined. The isDefined() method is called when values are writ-
ten to the DataModel to ensure that the program tree reflects the proper state of the program node.
Finally, generateScript(ScriptWriter) is called to add script code to the spot where the underlying
node occurs in the robot program.
As the user interacts with the text input field, the constructed message is displayed on the
screen using the view instance. Each Hello World node is defined (green) if both the Hello
World installation node is defined and the name in the program node is non-empty. When exe-
cuted, it shows a simple popup dialog with the title defined in the installation and the message
constructed from the name.
The popup title is the value of the script variable hello_world_swing_popup_title. This variable is
initialized by the script code contributed by the Hello World installation node. Thus, the script
variable serves to pass data from the contributed installation node to the contributed program
node. Another approach to pass information between these two objects is by directly requesting
the installation object through the ProgramAPI interface. The Hello World program node utilizes
this approach in its updatePopupMessageAndPreview() method.
Furthermore, if the URCap contains an installation node contribution, the data model of the
installation contribution not be modified from the program node contribution. If such modifi-
cations occur in the call to openView() while a program is running, PolyScope will automatically
stop the program every time the URCap program node is encountered in the program tree,
because the installation was changed. It is, however, allowed to read from the data model in
the installation node contribution.
Only if the end user initiated the action (e.g. clicked a button) should the changes be recorded
as an undoable action. Otherwise the end user will be able to undo something he did not do
As mentioned in section 8.3. Making the customized Program Nodes available to PolyScope,
remember to only store data related to the current configuration of the particular program node
instance in the model, i.e. no global or shared state, state irrevelant to this node instance, etc.
should be stored there.
Undoable actions only apply to a program nodes. Changes to a data model in an installation
node do not have undo/redo support and will not throw a IllegalStateException exception.
When a user clicks undo, the previous values are restored in the data model as well as the
program tree and a call to the openView() method if the program node is currently selected, for
an opportunity to display the new values.
This also means that no values should be cached in member variables, but always retrieved from
the data model, as there is no guarantee that things have not changed. Also keep in mind, that
the user might not select the Command tab of the URCap, so there is no guaranteed call to
openView(). This can be the case when loading a program that has already been setup.
When a contributable program node is created in PolyScope by the user, the data model ob-
ject given to the createNode(ProgramAPIProvider, SwingProgramNodeView, DataModel, CreationContext)
method is empty and the provided CreationContext object will also reflect this.
When a program is loaded, the method createNode(...) is called for each persisted program node
contribution to re-create the program tree. In contrast to newly creating the program node, the
data model now contains the data from the persisted node and the CreationContext object will
reflect this situation also. All modifications to the data model from the program node construc-
tor are ignored. This means that ideally the program node constructor should not set anything
in the data model.
For creating sub-trees a program model can be used. In some of the URCap examples in
Chapter 10. URCap Examples Overview it is demonstrated how a sub-tree can be generated
programmatically. The program model provides the interface TreeNode to create and manipu-
late the sub-tree. When a contributable program node is created in PolyScope by the user, the
tree node has no children. The program model can be requested through the ProgramAPI interface.
When a program is loaded each program node is deserialized on its own, this includes sub-trees
previously created through the program model. Also now, the tree node requested through
the ProgramModel is empty. The program node factory returned by getProgramNodeFactory() in
the ProgramModel interface will return program nodes without any functionality. In particu-
lar, the method createURCapProgramNode(Class<? extends URCapProgramNodeService>) does not call the
createNode(...) method in the specified service. Therefore, modifications are ignored during the
createNode(...) call.
This means that Java garbage collection has a chance to clean up previous instances in case
any listeners have been forgotten or other potential memory leaks have been created. This also
means that no references to the view instance or contribution instance should be kept outside
these classes. Respecting this will protect PolyScope from running out of memory.
9 Contribution of a Daemon
A daemon can be any executable script or binary file that runs on the control box. The My
Daemon Swing URCap serves as the running example for explaining this functionality and is an
extension of the Hello World Swing example. The My Daemon Swing example offers the same
functionality from the user’s point of view as the Hello World Swing example.
However, the My Daemon Swing URCap performs its tasks through an executable, which acts
as a sort of driver or server. The executable is implemented as Python 2.5 script and C++
binary. The executables communicate with the Java front-end and URScript executor through
XML encoded Remote Procedure Calls (XML-RPC). Figure 3, page 14, shows the structure of
the My Daemon Swing URCap project.
The /etc/service directory contains links to the URCap daemon executables currently running.
If a daemon executable has a link present but is in fact not running, the ERROR state will be
returned upon querying the daemon’s state. The links to daemon executables follow the lifetime
of the encapsulating URCap and will be removed when the URCap is removed. The initial state
for a daemon is STOPPED, however if it is desired, auto-start can be achieved by calling start() in
the init(DaemonContribution) method right after the daemon has had its resources installed.
20 try {
21 d a e m o n C o n t r i b u t i o n . i ns ta ll R es ou rc e ( new URL ( " file : com / ur / urcap / examples /
mydaemonswing / impl / daemon / " ) ) ;
22 } catch ( M a l f o r m e d U R L E x c e p t i o n e ) { }
23 }
24
25 @Override
26 public URL getExecutable () {
27 try {
28 // Two equivalent example daemons are available :
29 return new URL ( " file : com / ur / urcap / examples / mydaemonswing / impl / daemon /
hello - world . py " ) ; // Python executable
30 // return new URL (" file : com / ur / urcap / examples / mydaemonswing / impl / daemon /
HelloWorld ") ; // C ++ executable
31 } catch ( M a l f o r m e d U R L E x c e p t i o n e ) {
32 return null ;
33 }
34 }
35
36 public D a e m o n C o n t r i b u t i o n getDaemon () {
37 return d a e m o n C o n t r i b u t i o n ;
38 }
39
40 }
Log information with respect to the process handling of the daemon executable are saved
together with the daemon executable (follow the symbolic link of the daemon executable in
Note, that script daemons must include an interpreter directive at the first line to help select
the right program for interpreting the script. For instance, Bash scripts use "#!/bin/bash" and
Python scripts use "#!/usr/bin/env python".
The daemon runs in parallel with PolyScope and can in principle change its state independently.
Therefore, the label below the buttons displays the current run status of the daemon. This label
is updated with a 1 Hz frequency, utilizing the java.util.Timer class. Since the UI update is
initiated from a different thread than the Java AWT thread, the timer task must utilize the
EventQueue.invokeLater functionality. Note, the Timer is added when the My Daemon Installation
screen is opened (see openView()) and removed when the user moves away from the screen (see
closeView()) to conserve computing resources.
Two options are available for Java and URScript to communicate with the daemon:
• TCP/IP sockets can be used to stream data.
• XML encoded Remote Procedure Calls (XML-RPC) can be used for configuration tasks
(e.g. camera calibration) or service execution (e.g. locating the next object).
The advantage of XML-RPC over sockets is that no custom protocol or encoding needs to
be implemented. The URScript XML-RPC implementation supports all URScript data types.
Moreover, a RPC will only return when the function execution has been completed. This is
desirable when the next program step relies on data retrieved from the daemon service. Plain
sockets are on the other hand more efficient for data streaming, since there is no encoding
overhead. Both methods can be complimentary applied and are available for Java ↔ daemon
and URScript ↔ daemon communication.
Listing 10 shows a small URScript example for making a XML-RPC call to a XML-RPC server.
The hello-world.py example daemon (see Listing 22, page 70) can be used as XML-RPC test
server. Simply start the daemon in the My Daemon and run the URScript in a Script node.
The intention of this URScript example is to retrieve a message from the daemon to display
during runtime (similar to the My Daemon program node). The rpc_factory script function
creates a connection to the XML-RPC server in the daemon. The new connection is stored in
the global my_daemon_swing variable and serves as a handle. The next line then requests the XML-
RPC server in the daemon to execute the get_message(...) function with the string argument
"Bob" and return the result. The return value of the RPC call is stored in the mydaemon_message
variable for further processing in the popup(...) script function.
Note, making XML-RPC calls from URScript does not require any additional function stubs or
pre-definitions of the remote function to be executed in URScript. Until the XML-RPC returns
this URScript thread is automatically blocked (i.e. no sync nor Wait is needed). The standard
XML-RPC protocol does not allow void return values and XML-RPC extensions enabling this
are not always compatible.
The My Daemon Swing example also includes a Java XML-RPC client example, see the combi-
nation of the MyDaemonProgramNodeContribution and XMLRPCMyDaemonInterface classes (Listing 20, page
65 and listing 21, page 67 respectively). Note, the execution of the XML-RPC calls is not on
the main Java AWT thread, but offloaded to a separate thread.
If the first line is not printed directly after installing the SDK, please reboot your PC for the
environment variables to be updated.
The My Daemon Swing URCap comes with a fully functional C++ XML-RPC server example
that is equivalent to the hello-world.py Python daemon. Simply switch the comments in the
function getExecutable() in the MyDaemonDaemonService class (Listing 9, page 33), and recompile to
use the C++ daemon implementation. The popup title should now be appended with "(C++)"
instead of "(Python)" during execution of the URCap.
The C++ daemon directory structure is shown in Figure 6, page 37. For managing the software
construction process of the C++ daemon a tool called Scons is used. The SConstruct file among
other things contains the main configuration, the urtool3 cross-compiler, and libxmlrpc-c inte-
gration. The SConscript files are used to define the compilation targets, e.g. the Hello World
binary.
com.ur.urcap.examples.mydaemonswing
...
daemon
service
AbyssServer.cpp
AbyssServer.hpp
SConscript
XMLRPCMethods.cpp
XMLRPCMethods.hpp
Data.cpp
Data.hpp
HelloWorld.cpp
HelloWorld.cpp
SConscript
SConstruct
...
For the example URCap, the daemon will be build as part of the URCap build process by maven.
However, the daemon can also be compiled manually by typing the following in a terminal:
This will build a release version of the daemon. Using release=0 will build an executable with
debugging symbols.
The XML-RPC functionality in the C++ daemon relies on the open-source library libxmlrpc-
c (http://xmlrpc-c.sourceforge.net). This library is by default available on the CB3.0/3.1
and CB5.0 control boxes. The service directory contains all relevant XML-RPC code. The
AbyssServer is one of the XML-RPC server implementations supported by libxmlrpc-c. Please
look in the C++ code for more programming hints and links to relevant documentation.
• MyDaemonInstallationNodeService
• MyDaemonProgramNodeService
• MyDaemonDaemonService
• The UI
• Persistence of program and installation files
• Creation and execution of programs
• Program undo/redo functionality
Information:
• Available from:
– URCap API version 1.3.0.
My Daemon Swing is an extension to the Hello World Swing URCap and demonstrates how
a Python 2.5 or C++ daemon can be integrated with the URCap Software Platform. This
is useful when a URCap depends on e.g. a driver or server which is not implemented in
Java. Furthermore, the URCap shows how the XML-RPC protocol can be used to com-
municate with the daemon from an installation node and in the script code generated by
a program node.
Information:
• Available from:
– URCap API version 1.3.0.
– PolyScope version 3.6.0/5.0.4.
• Main API interfaces: DaemonContribution, DaemonService.
Script Function Swing demonstrates how to add functions to the list of available script func-
tions in the Expression Editor. Script functions often used by end users of a URCap should
be added to this list.
Information:
• Available from:
– URCap API version 1.3.0.
Pick or Place Swing is a toy example that shows how to make changes to the program tree
through the TreeNode API. The program node service (SwingProgramNodeService inter-
face) is configured in such a way that it creates program node contributions that can only
be inserted into the program tree by a URCap and not from the UI of PolyScope by the
end user.
Information:
• Available from:
– URCap API version 1.3.0.
– PolyScope version 3.6.0/5.0.4.
• Main API interfaces: ProgramModel, TreeNode, ProgramNodeFactory, SwingPro-
gramNodeService, ContributionConfiguration
Ellipse Swing is a toy example, where a pose is used to define the center point for an ellipse-
like movement. The movement is achieved by inserting a pre-configured MoveP program
Note:
• The functionality of assigning the Waypoint nodes custom names is only available
from URCap API version 1.4.0 (released with PolyScope version 3.7.0/5.1.0)
• Requesting the user to move the robot to a defined center point is only available from
URCap API version 1.5.0 (released with PolyScope version 3.8.0/5.2.0).
• From URCap API version 1.6.0 (released with PolyScope version 3.9.0/5.3.0) the use
of the deprecated move node config factory (MoveNodeConfigFactory interface) has
been replaced with the equivalent builder and the TCP selection of the MoveP node
is pre-configured.
• Support for allowing the end user to start from and break on child nodes is only
available from URCap API version 1.9.0 (released with PolyScope version 5.6.0).
• From URCap API version 1.11.0 (released with PolyScope 3.14.0/5.9.0) the use of
the deprecated method getUserDefinedRobotPosition(RobotPositionCallback) has been re-
placed with the equivalent getUserDefinedRobotPosition(RobotPositionCallback2) method.
Furthermore, the use of the deprecated factory method for creating fixed position
Waypoint node configurations (createFixedPositionConfig(...) without TCP offset) has
been replaced with the equivalent method taking the TCP offset as parameter as well.
Information:
• Available from:
– URCap API version 1.3.0.
– PolyScope version 3.6.0/5.0.4.
• Main API interfaces: UserInteraction, RobotPositionCallback2, RobotMovement,
RobotMovementCallback, WaypointNodeConfig, MovePMoveNodeConfig, MoveN-
odeConfigBuilders, MovePConfigBuilder, PoseFactory, Pose, SimpleValueFactory, Joint-
Positions
Cycle Counter Swing demonstrates how to work with variables. In this example, the chosen
variable will be incremented each time the program node is executed.
This sample also demonstrates how to allow the end user to use the builtin PolyScope sup-
port for starting from and pausing/breaking on a selected program node in the program
tree. In this case, the end user can start from or break on any child node under the Cycle
Counter (URCap) program node.
Note:
• Support for allowing the end user to start from and break on child nodes is only
Idle Time Swing demonstrates how to work with the ProgramNodeVisitor to traverse all pro-
gram nodes in a sub-tree. In this example, all Wait nodes will be visited. If a Wait node
is configured to wait for an amount of time, that amount of idle time (in seconds) will
accumulate in the selected variable.
Information:
• Available from:
– URCap API version 1.3.0.
– PolyScope version 3.6.0/5.0.4.
• Main API interfaces: ProgramNodeVisitor, WaitNodeConfig
Information:
• Available from:
– URCap API version 1.3.0.
User Input demonstrates how to work with the virtual on-screen keyboard/keypad and user
input validation.
Information:
• Available from:
– URCap API version 1.3.0.
– PolyScope version 3.6.0/5.0.4.
• Main API interfaces: KeyboardInput, InputValidationFactory
Information:
• Available from:
– URCap API version 1.3.0.
Node Ordering Swing demonstrates how to define a specific sort order in PolyScope for the
program node contributions from a URCap.
Information:
• Available from:
– URCap API version 1.5.0.
– PolyScope version 3.8.0/5.2.0.
• Main API interfaces: ContributionConfiguration, SwingProgramNodeService.
Tool Changer Swing is a toy example that shows how contribute TCPs to PolyScope as well
as access the list of available TCPs in PolyScope.
In the installation node contribution, the user can define a tool change position, enable/dis-
able different tool TCPs and define a translational offset between the tool flange and all
the enabled tool TCPs.
In the program node contribution, the user can select a TCP for the new tool from the
list of all available TCPs in PolyScope. When the program node is executed, the robot
will move to the user-defined tool change position, change the tool (simulated by a waiting
period) and finally change the active TCP to the selected TCP.
Information:
• Available from:
– URCap API version 1.5.0.
Note:
• From URCap API version 1.11.0 (released with PolyScope 3.14.0/5.9.0) the use of
the deprecated method getUserDefinedRobotPosition(RobotPositionCallback) has been re-
placed with the equivalent getUserDefinedRobotPosition(RobotPositionCallback2) method.
Move Until Detection Swing demonstrates how to work with the Distance and Until pro-
gram nodes through the URCap API.
The program node contribution creates a Direction node that moves the robot downwards,
until a sensor is triggered (through an input), or until a maximum distance is reached. If
the sensor is triggered, a connected physical device (e.g. a gripper) is activated by setting
an output high. If the maximum distance is travelled before the sensor is triggered, a
popup is generated to display an error to the user. The user can specify the maximum
distance through a text field in the Move Until Detection node.
Information:
• Available from:
Tool I/O Control Swing demonstrates how to work with the resource model and request
exclusive control of the Tool I/O Interface through the URCap API. It also demonstrates
how to check, if a specific capability is available on the underlying robot system (in this
case the Tool Communication Interface (TCI) and Tool Output Mode features).
For further details about resource control, please see the separate Resource Control doc-
ument. The concept of system capabilities and checking their availability is described in
the separate Capabilities document.
Information:
• Available from:
– URCap API version 1.7.0.
– PolyScope version 3.10.0/5.4.0.
• Main API interfaces: ResourceModel, ControllableResourceModel, ToolIOInterface-
Controller, ToolIOInterface, ToolIOInterfaceControllable, CapabilityManager.
Grip and Release Swing demonstrates how to use gripper devices in a template program
node.
In the program node contribution, the user can select a gripper among the registered
grippers available in PolyScope. When a gripper has been selected, the selected gripper
can be applied to the template node. This will insert two Gripper program nodes for
the selected gripper: one node configured for a grip action and one node configured for a
release action.
This example demonstrates how to:
Note: To have grippers to select from in the program node, some of the gripper driver
URCap samples (e.g. Simple Gripper) can be installed.
For further details about how to use devices in template nodes, please see the separate
Using a Device in a Template document.
Information:
• Available from:
– URCap API version 1.9.0.
– PolyScope version 3.12.0/5.6.0.
• Main API interfaces: GripperManager, GripperProgramNodeFactory, GripperNode,
Create Feature Swing is an example that shows how to contribute a feature to PolyScope as
well as how to modify and remove it.
In the installation contribution, the user can define a feature and subsequently update and
remove it. The created feature is stored in the data model of the installation contribution.
In the program node contribution, the user will be asked to create a feature from the instal-
lation contribution, if not done already. When the feature is created, the user can press a
button to create a movement relative to the created feature. The movement is achieved by
inserting a pre-configured MoveL program node containing pre-defined Waypoint nodes.
When the program node is executed, the robot will perform a square movement centered
at the created feature.
Information:
• Available from:
– URCap API version 1.9.0.
– PolyScope version 3.12.0/5.6.0.
• Main API interfaces: FeatureContributionModel.
Note:
• From URCap API version 1.11.0 (released with PolyScope 3.14.0/5.9.0) the use of
the deprecated method getUserDefinedRobotPosition(RobotPositionCallback) has been re-
placed with the equivalent getUserDefinedRobotPosition(RobotPositionCallback2) method.
Furthermore, the use of the deprecated factory method for creating fixed position
Waypoint node configurations (createFixedPositionConfig(...) without TCP offset) has
been replaced with the equivalent method taking the TCP offset as parameter as well.
The tool flange is used as the TCP offset.
Create Payload Swing is an example that shows how to contribute a payload to PolyScope
as well as how to modify and remove it.
In the installation contribution, the user can create a payload and subsequently update the
mass and center of gravity (CoG) parameters of the created payload as well as remove the
payload. Values entered by the user for the mass and center of gravity (CoG) parameters
are validated using the parameter limits available through the API. The created payload
is stored in the data model of the installation contribution.
In the program node contribution, the user will be asked to create a payload from the
installation contribution, if not done already. When the payload is created, the user can
press a button to create a Set Payload node with the payload (created in the installation
contribution) pre-selected.
Information:
• Available from:
– URCap API version 1.13.0
– PolyScope version 5.11.0
• Main API interfaces: PayloadContributionModel, RobotLimits, SetPayloadNode
The URCap also shows how to use the filler UI element for controlling/grouping the layout
and how to add a custom input validator for detecting errors for enterable user inputs, in
this case an IP address user input (text field).
Information:
• Available from:
– URCap API version 1.7.0.
– PolyScope version 3.11.0/5.5.0.
• Main API interfaces: CustomUserInputConfiguration.
Simple Gripper demonstrates how to create a gripper driver contribution for a basic gripper
that only supports the mandatory ”default” grip and release actions. The URCap uses
digital outputs in the script code generation to trigger grip and release actions.
For further details about gripper driver contributions, please see the separate Gripper
Driver document.
Information:
• Available from:
– URCap API version 1.8.0.
– PolyScope version 3.11.0/5.5.0.
Advanced Gripper demonstrates how to create a gripper driver contribution for a more ad-
vanced gripper that supports some of the optional gripper capabilities and controls the
Output Voltage setting of the Tool I/O Interface resource. The URCap shows how to:
• Configure gripper capabilities for, e.g. width, speed, force, vacuum and feedback for
grip and release detection
• Request exclusive control of the Tool I/O Interface resource
• Configure the Output Voltage I/O setting of the Tool I/O Interface
Additional information:
• For details about gripper driver contributions, please see the separate Gripper Driver
document.
• For information about system resource control, please see the separate Resource Con-
trol document.
• To see an example of how some of the other settings of the Tool I/O Interface can be
configured, see the Tool I/O Control Swing URCap example.
• Available from:
– URCap API version 1.8.0.
– PolyScope version 3.11.0/5.5.0.
• Main API interfaces: GripperContribution, GripperCapabilities, SystemConfigu-
ration, GripActionParameters, ReleaseActionParameters, GripperFeedbackCapabili-
ties.
Custom Gripper Setup demonstrates how to create a gripper driver contribution which de-
fines a custom UI in the installation node for setting up the gripper as well as adds a TCP
for the gripper to PolyScope.
For further details about gripper driver contributions, please see the separate Gripper
Driver document.
Information:
• Available from:
– URCap API version 1.8.0.
– PolyScope version 3.11.0/5.5.0.
• Main API interfaces: GripperContribution, TCPConfiguration, CustomUserInput-
Configuration.
Dual Zone Gripper demonstrates how to create a gripper driver contribution for a multi-
gripper that supports multiple permanently enabled, individual grippers as well as dynam-
ically adjusts the parameters of a capability (after it has been registered) for all individual
grippers.
In this example, the gripper is a vacuum dual gripper with two independent physical grip-
pers (zones) ”built into” the gripping device. The URCap provides the user the option
to select from three individual zones (grippers), named Zone A, Zone B and Zone A+B.
The user can operate Zone A and Zone B independently of one another or choose to use
both zones at the same by selecting Zone A+B.
A custom UI is defined in the installation node which allows the user to enable the ”Use
Fragile Handling” option. When enabled, this will restrict the maximum vaccum level
available for a grip action. This is achieved by reducing the maximum value and default
value parameters of the registered vacuum capability simultaneously for all zones.
For further details about gripper driver contributions, please see the separate Gripper
Driver document.
Information:
• Available from:
– URCap API version 1.11.0.
– PolyScope version 3.14.0/5.9.0.
• Main API interfaces: GripperContribution, GripperCapabilities, GripperListProvider,
GripperListBuilder, SelectableGripper, SystemConfiguration, TCPConfiguration, Grip-
VacuumCapability
Dynamic Multi-Gripper demonstrates how to create a gripper driver contribution that sup-
ports both a single gripper setup and multi-gripper setup as well as dynamically adjusts
the parameters of a capability (after it has been registered) exclusively for each individual
gripper.
In this example, the gripper driver provides the user the option of choosing between us-
ing a setup with only a single gripper mounted on the robot, or one where two separate,
identical grippers are mounted on the robot at the same time. Each individual gripper
supports moving to a user configurable position (open/close to a configurable width).
A custom UI is defined in the installation node which allows the user to select how many
gripper are mounted on the robot. The available options are Single and Dual. Selecting
the Single option will disable the secondary gripper, Gripper 2, and only the standard
gripper, Gripper 1, will be available to the user. Choosing the Dual option will enable
both grippers making both of them available to the user. Depending on the option se-
lected, the offset of TCP for the standard gripper (Gripper 1 ) is updated accordingly.
For each individual gripper (Gripper 1 and Gripper 2 ), the user can configure the type of
fingertips attached to the gripper (in the installation node). There are two options, Stan-
dard and Extended. The Standard option is the default whereas the Extended option can
be selected, when fingertips with wide range is attached. Selecting the Extended option
will increase the maximum value and default value parameters of the registered width ca-
pability exclusively for the specific individual gripper (independently of the other gripper).
Information:
• Available from:
– URCap API version 1.11.0.
– PolyScope version 3.14.0/5.9.0.
• Main API interfaces: GripperContribution, GripperCapabilities, GripperListProvider,
GripperListBuilder, SelectableGripper, MultiGripperCapability, SystemConfiguration,
TCPConfiguration, WidthCapability
Simple Screwdriver demonstrates how to create a screwdriver driver contribution for a basic
screwdriver that only supports the mandatory start and stop screwdriver operations. The
URCap uses digital outputs in the script code generation to start and stop the screwdriver.
For further details about screwdriver driver contributions, please see the separate Screw-
driver Driver document.
Information:
• Available from:
– URCap API version 1.9.0.
– PolyScope version 5.6.0.
For further details about screwdriver driver contributions, please see the separate Screw-
driver Driver document.
Information:
• Available from:
– URCap API version 1.9.0.
– PolyScope version 5.6.0.
Custom Screwdriver demonstrates how to create a screwdriver contribution driver which de-
fines a custom UI in the Screwdriving installation node for setting up the screwdriver as
well as adds a default TCP for the screwdriver to PolyScope.
For further details about screwdriver driver contributions, please see the separate Screw-
driver Driver document.
Information:
This prompts you with a dialog box where you select a group and artifact-id for your new
URCap:
Next you must specify which Universal Robots robot series your URCap is compatible with:
Maybe your URCap requires specific hardware and/or software features, which are only available
on a specific robot series (e.g., e-Series robots only), or the URCap might be compatible with
all robot series. Such considerations should determine your choice here. See section 12.1. Robot
Series Compatibility for more details about robot series compatibility.
Finally, you must also specify the target URCap API version. Choosing an earlier version
of the API will make your URCap compatible with earlier PolyScope versions, but also limit
the functionality accessible through the API. URCap API compatibility is described in section
12.2. API Compatibility.
Pressing the Ok button in the last dialog creates a new Maven project under the sub-folder
Notice that the generated pom.xml file has a section with a set of properties for the new URCap
with meta-data for vendor, contact address, copyright, description, and short license informa-
tion which will be displayed to the user when the URCap is installed in PolyScope. This section
also contains boolean compatibility flag properties specifying the URCap’s compatibility with
the different available robot series. Update the URCap meta-data section with the data relevant
for the new URCap. See Listing 1 for an example of how this section might look.
Should you need to change the version of the URCap API to depend upon after your project
has been setup, this can be done in the pom.xml file in your project. Here you must update
to the desired version in the URCap API dependency under the <dependencies> section of the
pom.xml-file as well as modify the <import-package> element under the maven-bundle-plugin if version
information is present. See listings 12, 13 and 16 for examples of this
12 Compatibility
12.1 Robot Series Compatibility
When developing URCaps you must mandatorily specify which robot series your URCap is com-
patible with, i.e. which robot series your URCap can run on. The purpose is to provide a good
user experience for the URCap installation process by ensuring that end users cannot install
a URCap that is not compatible with their robot and/or Universal Robots offline simulator
software (URSim).
When specifying your URCap’s compatibility, you need to consider, if your URCap depends on
specific hardware and/or software functionality, which is not available on all robot series (e.g.,
only available on e-Series robots), or if the URCap can run on all available robot series. Note
that your URCap’s compatibility must be specified for all robot series available in the line of
robots from Universal Robots (including robot series which your URCap is incompatible with).
These two properties specify the URCap’s compatibility with the CB3 and the e-Series robot
series, respectively. Acceptable values for the properties are either true or false. A value of true
indicates that the URCap can be installed and run on the given robot series, and a value of
false indicates otherwise. Note that both flags must present including the flag for a robot series
which your URCap is not compatible with.
Using the newURCap.sh script mentioned in section 11. Creating new thin Projects using a Maven
Archetype, the compatibility flags are handled automatically for you. On the other hand, if an
existing URCap needs to be updated to support the compatibility flags, you must manually
update your URCap project’s pom.xml file. This will only be necessary, if you used a version of
the URCap SDK lower than 1.12.0 to create the URCap by either using the newURCap.sh script,
or by basing the URCap on a modification of one of the URCap example projects included in
the SDK. The required changes are described in subsection 12.1.3. Updating Existing URCaps.
If the compatibility of your URCap changes, remember to update the value of the URCap’s
compatibility flags accordingly.
Similarly, a URCap’s compatibility flags are examined during startup of PolyScope, if the UR-
Cap was deployed and installed using Maven (described in section 6. Deployment with Maven)
instead of being installed through the PolyScope UI. If the URCap is incompatible, it will not
be allowed to start and an error dialog will be displayed in PolyScope.
The presence of the compatibility flags in a URCap will be verified during startup of PolyScope
(if the URCap was deployed and installed using Maven), and a URCap will not be started, if
any of the flags are missing. Likewise, a URCap with missing compatibility flag(s) will not be
installable through PolyScope. An error dialog will be displayed in PolyScope in these cases.
Note that only URCaps depending on URCap API version 1.12.0 or higher will be prevented
from starting or being installed, if the compatibility flags are not present.
First of all, you must add the two compatibility flag properties, urcap.compatibility.CB3 and
urcap.compatibility.eSeries, to the URCap meta-data section as shown in Listing 14 in section
12.1.1. Compatibility Flags. Remember to specify boolean values corresponding to your UR-
Cap’s compatibility.
Listing 15: Excerpt of the pom.xml file’s Maven bundle plugin configuration with compatiblity
flag support
1 ...
2 < build >
3 < plugins >
4 ...
5 < plugin >
6 < groupId > org . apache . felix </ groupId >
7 < artifactId > maven - bundle - plugin </ artifactId >
8 ...
9 < configuration >
10 < instructions >
11 <! - -******** DO NOT MODIFY THE ENTRIES OF THIS SECTION ******** - - >
12 < Bundle - Category > URCap </ Bundle - Category >
13 ...
14 < URCapCompatibility - CB3 > $ { urcap . compatibility . CB3 } </
URCapCompatibility - CB3 >
15 < URCapCompatibility - eSeries > $ { urcap . compatibility . eSeries } </
URCapCompatibility - eSeries >
16 <! - -*********************************************************** - - >
17 ...
18 < instructions >
19 < configuration >
20 < plugin >
21 ...
22 < plugins >
23 ...
In order for PolyScope to be able to read the specified values for the compatibility flag proper-
ties, the <build> section with configuration of build plugins also needs to be updated. Here the
configuration for the Maven bundle plugin must be updated to contain two new instructions,
<URCapCompatibility-CB3> and <URCapCompatibility-eSeries>, as shown in the excerpt in Listing 15.
For convenience, instead of manually making the changes described above, you can use the
upgradeURCap.sh script to update your pom.xml file.
Below are examples of usage of the script, where /home/myuser/myURCap is the path to the folder
with a URCap project and /home/myuser/sdk is the path to the folder of the URCap SDK.
This will apply the required changes to the URCap project’s pom.xml file inside the folder
/home/myuser/myURCap. Both compatibility flag properties are assigned the boolean value of
true by default (i.e. your URCap is compatible with both the CB3 and e-Series robot series).
Remember to change the value of the compatibility flag properties in the pom.xml file accord-
ingly, if the preassigned default values does not match your URCap’s compatibility.
Alternatively, it is also possible to copy the upgradeURCap.sh script to your URCap project folder
and execute the script from within that folder by typing:
1 $ cd myURCap
2 $ ./ upgradeURCap . sh .
Finally, you have the option of directly specifying the boolean value for the compatibility flags
using the compatibilityCB3 and compatibilityESeries arguments:
1 $ ./ upgradeURCap . sh ../ myURCap c o m p a t i b i l i t y C B 3 = false c o m p a t i b i l i t y E S e r i e s =
true
In this case, you have specified that your URCap is compatible with the e-Series robot series,
but incompatible with the CB3 robot series.
A given version of the API is compatible with a specific version of PolyScope (see table 1 below).
PolyScope will remain backwards compatible with earlier versions of the API. This means that
if you choose to use the newest API, customers using your URCap must be running at least the
It is not a problem if the customer is running a newer (future) version of PolyScope. However,
it is not possible for the customer to use your URCap if he is running an earlier version of
PolyScope than the one the API was released with. A good rule of thumb is thus to choose the
earliest possible version of the API that supports the functionality you wish to use. This will
target the broadest audience.
For instance, if you specify a dependency on the API version 1.1.0, your URCap will only run on
PolyScope version 3.4.0 or newer. If you wish to target the broadest possible audience, you must
use version 1.0.0 of the API and the customers must be running PolyScope version 3.3.0 or newer.
Note that API version 1.12.0 is the final API version supported on CB3 robots. Higher API
versions are only available on e-Series robots.
As an example you could have a dependency on API version 1.5.0 and run it on PolyScope
version 3.7.0/5.1.0 (officially only supporting API version 1.4.0), but in the actual execution
path you can only use types present in API version 1.4.0.
To have the URCap resolve at runtime the pom.xml must have the option ;resolution:=optional
appended to the URCap API entry in the <import-package> section. The full <import-package>
section could look like this:
This will make the URCap start up regardless of what the actual dependency states.
As mentioned you must also structure your code so no code referring unsupported API function-
ality is executed. Also no import or catch clauses referring to unsupported types can be present
in classes that will execute. See listings 17 and 18 for an example of how to structure this. In
listing 17 all code related to unsupported API functionality is located and in 18 a check for
PolyScope version number is performed before creating an instance of the AdvancedFeature class.
If you choose to use this advanced feature, you must test your URCap carefully on all PolyScope
versions you wish to support making sure all code execution paths are tested.
13 Exception Handling
All exceptions thrown and not caught in a URCap will be caught by PolyScope. If this happens
when the end user selects either an installation node or a program node from the URCap, the
UI provided by the URCap will be replaced by a screen displaying information about the error.
In all other cases, a dialog will be shown to the end user.
The error screen and dialog will show that an exception has happened in the URCap along with
meta information about the URCap. This occurs if the call stack originates from PolyScope (i.e.
if the exception occurred when PolyScope called a method defined in a URCap API interface
implemented by the URCap) or happens inside PolyScope (e.g. due to illegal arguments passed
in an API method call). In this case, it will also contain a section showing the stack trace from
the exception.
14 Troubleshooting
Internally in PolyScope, a URCap is installed as an OSGi bundle with the Apache Felix OSGi
framework. For the purpose of debugging problems, it is possible to inspect various information
about bundles using the Apache Felix command shell.
You can establish a shell connection to the running Apache Felix by opening a TCP connection
on port 6666. Access the Apache Felix shell console by typing:
1 $ nc 127.0.0.1 6666
2
3 Felix Remote Shell Console :
4 ============================
5
6 ->
Note that you need to use the nc command, since the telnet command is not available on the
robot, and 127.0.0.1 because localhost does not work on a robot.
To view a list of installed bundles and their state type the following command:
1 -> ps
2 START LEVEL 1
3 ID State Level Name
4 [ 0] [ Active ] [ 0] System Bundle (5.2.0)
Inside the shell you can type help to see the list of the available commands:
1 -> help
2 uninstall
3 sysprop
4 bundlelevel
5 find
6 version
7 headers
8 refresh
9 start
10 obr
11 inspect
12 ps
13 stop
14 shutdown
15 help
16 update
17 install
18 log
19 cd
20 startlevel
21 resolve
22
23 Use ’ help < command - name > ’ for more information .
24 ->
For example, the headers command can be executed to display different properties of the indi-
vidual installed bundles.
The following example is taken from a small program using the Hello World Swing URCap.
To see the generated script code for an URCap, it is required to save the program. Assuming
the program is called hello.urp, the corresponding script code can be found in hello.script.
The script code of the installation node will be surrounded with begin/end URCap comments
and information about the source of the URCap and its type:
1 ...
2 # begin : URCap Installation Node
3 # Source : Hello World Swing , 1.0.0. SNAPSHOT , Universal Robots
4 # Type : Hello World Swing
5 h e l l o _ w o r l d _ s w i n g _ p o p u p _ t i t l e = " Hello World "
6 # end : URCap Installation Node
7 ...
Similarly for program nodes, script code is surrounded with begin/end URCap comments and
information about the source of the URCap and its type as shown below. The program node
generated by PolyScope is the first label after the # begin: URCap Program Node, here $ 2. The
Listing 19: Java class defining functionality for the My Daemon installation node
1 package com . ur . urcap . examples . mydaemonswing . impl ;
2
3 import com . ur . urcap . api . contribution . D a e m o n C o n t r i b u t i o n ;
4 import com . ur . urcap . api . contribution . I n s t a l l a t i o n N o d e C o n t r i b u t i o n ;
5 import com . ur . urcap . api . contribution . installation . Cr ea ti o nC on te x t ;
6 import com . ur . urcap . api . contribution . installation . I n s t a l l a t i o n A P I P r o v i d e r ;
7 import com . ur . urcap . api . domain . data . DataModel ;
8 import com . ur . urcap . api . domain . script . ScriptWriter ;
9 import com . ur . urcap . api . domain . u s er in te r ac ti o n . in p ut va l id at io n .
InputValidationFactory ;
10 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d I n p u t C a l l b a c k ;
11 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d I n p u t F a c t o r y ;
12 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d T e x t I n p u t ;
13
14 import java . awt . EventQueue ;
15 import java . util . concurrent . Executors ;
16 import java . util . concurrent . S c h e d u l e d E x e c u t o r S e r v i c e ;
17 import java . util . concurrent . Sc h ed ul ed F ut ur e ;
18 import java . util . concurrent . TimeUnit ;
19
20 public class M y D a e m o n I n s t a l l a t i o n N o d e C o n t r i b u t i o n implements
InstallationNodeContribution {
53 }
54
55 @Override
56 public void openView () {
57 view . setPopupText ( getPopupTitle () ) ;
58 d a e m o n S t a t u s M o n i t o r . s t a r t M o n i t o r T h r e a d () ;
59
60 // UI updates from non - GUI threads must use EventQueue . invokeLater ( or
Swin gUtiliti es . invokeLater )
61 Runnable u p d a t e U I R un n a b l e = new Runnable () {
62 @Override
63 public void run () {
64 EventQueue . invokeLater ( new Runnable () {
65 @Override
66 public void run () {
67 updateUI () ;
68 }
69 }) ;
70 }
71 };
72 if ( s c h e d u l e A t F i x e d R a t e != null ) {
73 s c h e d u l e A t F i x e d R a t e . cancel ( true ) ;
74 }
75 s c h e d u l e A t F i x e d R a t e = e xe cu to r Se rv ic e . s c h e d u l e A t F i x e d R a t e ( updateUIRunnable
, 0 , 1 , TimeUnit . SECONDS ) ;
76 }
77
78 @Override
115
116 view . se tStatusL abel ( text ) ;
117 }
118
119 public void onStartClick () {
120 model . set ( ENABLED_KEY , true ) ;
121 a p p l y D e s i r e d D a e m o n S t a t u s () ;
122 }
123
124 public void onStopClick () {
125 model . set ( ENABLED_KEY , false ) ;
126 a p p l y D e s i r e d D a e m o n S t a t u s () ;
127 }
128
129 private void a p p l y D e s i r e d D a e m o n S t a t u s () {
130 new Thread ( new Runnable () {
131 @Override
132 public void run () {
133 if ( M y D a e m o n I n s t a l l a t i o n N o d e C o n t r i b u t i o n . this . i sD ae mo n En ab le d () ) {
134 // Download the daemon settings to the daemon process on initial
start for real - time preview purposes
135 try {
136 M y D a e m o n I n s t a l l a t i o n N o d e C o n t r i b u t i o n . this . a w a i t D a e m o n R u n n i n g () ;
137 d a e m o n S t a t u s M o n i t o r . setTitle ( M y D a e m o n I n s t a l l a t i o n N o d e C o n t r i b u t i o n .
this . getPopupTitle () ) ;
138 } catch ( Exception e ) {
139 System . err . println ( " Could not set the title in the daemon process .
");
177 keyboardInput . s e t E r r o r V a l i d a t o r ( i n p u t V a l i d a t i o n F a c t o r y .
c r e a t e S t r i n g L e n g t h V a l i d a t o r (1 , 255) ) ;
178 keyboardInput . s et In it i al Va lu e ( getPopupTitle () ) ;
179 return keyboardInput ;
180 }
181
182 public KeyboardInputCallback < String > g e t C a l l b a c k F o r T e x t F i e l d () {
183 return new KeyboardInputCallback < String >() {
184 @Override
185 public void onOk ( String value ) {
186 setPopupTitle ( value ) ;
187 view . setPopupText ( value ) ;
188 }
189 };
190 }
191
192 public boolean i s Po pu pT i tl eS et () {
193 return model . isSet ( P OP UP _T I TL E_ KE Y ) ;
194 }
195
196 private D a e m o n C o n t r i b u t i o n . State getDa emonStat e () {
197 return d a e m o n S t a t u s M o n i t o r . i s D a e m o n R e a c h a b l e () ? daemonService . getDaemon ()
. getState () : D a e m o n C o n t r i b u t i o n . State . STOPPED ;
198 }
199
200 private Boolean is Da e mo nE na b le d () {
201 return model . get ( ENABLED_KEY , true ) ;
202 }
Listing 20: Java class defining functionality for the My Daemon program node
1 package com . ur . urcap . examples . mydaemonswing . impl ;
2
3 import com . ur . urcap . api . contribution . P r o g r a m N o d e C o n t r i b u t i o n ;
4 import com . ur . urcap . api . contribution . program . P r o g r a m A P I P r o v i d e r ;
5 import com . ur . urcap . api . domain . data . DataModel ;
6 import com . ur . urcap . api . domain . script . ScriptWriter ;
7 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d I n p u t C a l l b a c k ;
8 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d I n p u t F a c t o r y ;
9 import com . ur . urcap . api . domain . u s er in te r ac ti o n . keyboard . K e y b o a r d T e x t I n p u t ;
10
11 import java . awt . EventQueue ;
12
13 public class M y D a e m o n P r o g r a m N o d e C o n t r i b u t i o n implements
ProgramNodeContribution {
14 private static final String NAME = " name " ;
15
16 private final P r o g r a m A P I P r o v i d e r apiProvider ;
17 private final M y D a e m o n P r o g r a m N o d e V i e w view ;
18 private final DataModel model ;
19 private final XmlRpcMyDaemonInterface daemonStatusMonitor ;
20 private final KeyboardInputFactory keyboardInputFactory ;
21
22 public M y D a e m o n P r o g r a m N o d e C o n t r i b u t i o n ( P r o g r a m A P I P r o v i d e r apiProvider ,
23 M y D a e m o n P r o g r a m N o d e V i e w view ,
24 DataModel model ) {
25 k e y b o a r d I n p u t F a c t o r y = apiProvider . g e t U s e r I n t e r f a c e A P I () .
g e t U s e r I n t e r a c t i o n () . g e t K e y b o a r d I n p u t F a c t o r y () ;
26 this . apiProvider = apiProvider ;
27 this . view = view ;
28 this . model = model ;
29 this . d a e m o n S t a t u s M o n i t o r = g et In st a ll at io n () . g e t D a e m o n S t a t u s M o n i t o r () ;
30
31 }
32
33 @Override
34 public void openView () {
35 view . setNameText ( getName () ) ;
36 d a e m o n S t a t u s M o n i t o r . s t a r t M o n i t o r T h r e a d () ;
37
38 // UI updates from non - GUI threads must use EventQueue . invokeLater ( or
Swin gUtiliti es . invokeLater )
39 updateUI () ;
40 }
41
42 private void updateUI () {
43 String title ;
44 String message ;
45 try {
46 // Provide a real - time preview of the daemon state
47 if ( d a e m o n S t a t u s M o n i t o r . i s D a e m o n R e a c h a b l e () ) {
48 title = d a e m o n S t a t u s M o n i t o r . getTitle () ;
49 message = d a e m o n S t a t u s M o n i t o r . getMessage ( getName () ) ;
50 } else {
88 }
89
90 @Override
91 public void ge nerateSc ript ( ScriptWriter writer ) {
92 // Interact with the daemon process through XML - RPC calls
93 // Note , alternatively plain sockets can be used .
94 writer . assign ( " m y d a e m o n _ m e s s a g e " , g et In s ta ll at i on () . g e t X M L R P C V a r i a b l e () +
" . get_message (\" " + getName () + " \") " ) ;
95 writer . assign ( " my daemon_ title " , ge tI n st al la t io n () . g e t X M L R P C V a r i a b l e () + " .
get_title () " ) ;
96 writer . appendLine ( " popup ( mydaemon_message , mydaemon_title , False , False ,
blocking = True ) " ) ;
97 writer . writeChildren () ;
98 }
99
100 public K e y b o a r d T e x t I n p u t g e t I n p u t F o r T e x t F i e l d () {
101 KeyboardTextInput keyboardTextInput = keyboardInputFactory .
c r e a t e S t r i n g K e y b o a r d I n p u t () ;
102 k e y b o a r d T e x t I n p u t . s et In i ti al Va l ue ( getName () ) ;
103 return k e y b o a r d T e x t I n p u t ;
104 }
105
106 public KeyboardInputCallback < String > g e t C a l l b a c k F o r T e x t F i e l d () {
107 return new KeyboardInputCallback < String >() {
108 @Override
109 public void onOk ( String value ) {
110 setName ( value ) ;
111 view . setNameText ( value ) ;