URCap - Principle of URCaps in PolyScope
This article is aimed at URCap Developers.
Principle of URCaps integration in PolyScope
This article outlines the principle, of how URCaps are integrated into the graphical programming interface PolyScope, and how the various Java-classes and interfaces interact.
A URCap is a software bundle, that is operating as a child process of PolyScope. Hence PolyScope will register the URCap, and interact with the URCap in various ways, e.g. during the startup of PolyScope, or based on user interactions in the user interface.
Each URCap can contribute multiple new functionalities to PolyScope, a such functionality is generally referred to as a service. A service could for instance be a Program Node, an Installation Node or a Toolbar.
As an example, a gripper URCap could include a service which is an Installation Node, e.g. for configuring the mounting of and connection to the gripper. Another service offered by this URCap could be a program node, which could be used to open the gripper, and a similar service, another program node, but for closing the gripper. The fourth service could be a toolbar, that allows live control of the gripper.
In this way, one URCap is offering 4 services to PolyScope; 1 Installation Node Service, 2 Program Node Services and 1 Toolbar Service.
The general principle of how a URCap is integrated into PolyScope, is shown on below illustration.
When PolyScope starts up, it looks for any installed URCaps. PolyScope then calls the Activator in each URCap, and the Activator will then register the Services, that should be available to PolyScope.
The Activator has two overwritten methods; start() and stop(). The start()-method is called when PolyScope starts, and the stop()-method is called when PolyScope stops.
The Service sets some general properties of the URCap, such as the name of the Node, shown in the user interface. The Service also exposes some methods, that PolyScope uses to create respectively the user interface (UI) and the contribution of each node.
In general, the settings present in the Service, can be considered rather static, as they are not available for changes runtime, but is only called at startup.
When the type of service is needed e.g. for a Program Node, when a user inserts it into a program, PolyScope calls the methods in the Service, to create this node. This will create a User Interface, and a Contribution.
The User Interface can either be created in Java Swing, or as a HTML user interface. In both cases it is important to note, that only exactly 1 instance of the UI will exist, regardless how many e.g. instance of the actual program node are inserted into the program.
The user interface contains the visual elements including styling shown to the user, in the layout of PolyScope, and when interacting with the UI, this will try to change the settings in the according Contribution.
The Contribution is the logic, or controller code, of the active node. There is a new instance of the Contribution, for each instance of the node, active in the program. The Contribution has a DataModel object, which is used to store the configuration of the node. When the user performs an action in the UI, the UI will report this to the active Contribution, which will then store this setting in the DataModel. When the user executes the program, the Contribution is responsible for generating the URScript based on the configuration of the DataModel.
When the nodes are created, and the user is interacting with it, the general activity is happening between the UI and the Contribution. The principle of how these two entities are linked, is elaborated later.
/Examples
In the following examples, we will focus on the Java Swing based UI. The selection of UI also has an impact on what Service should be created.
Below you can find a more explicit explanation of all required methods, that your URCap should implement, if you respectively create a:
/Principle of URCaps Program Node integration in PolyScope
You can find a general overview of how URCaps integrate into PolyScope in this article.
In the below illustration, some of the key methods required in respectively the Service, View (UI) and Contribution are shown.
/Swing Program Node Service
For a Java Swing based Program Node, the SwingProgramNodeService must be used.
The SwingProgramNodeService-interface dictates, that the below methods must be implemented:
getId()
Arguments: void
Returns: String
Called when: Once after registration of the Service
The getId()-method should return a unique ID for this kind of node. When multiple program nodes are present, this is used to identify what program nodes are of the same kind.
Example:
return “myNodeType”;
configureContribution()
Arguments: ContributionConfiguration configuration
Returns: void
Called when: Once after registration of the Service
The ContributionConfiguration represents the configuration of this kind of node. The ContributionConfiguration-object can be altered, by calling the associated methods, and can e.g. be used to toggle whether a program node can have children or not, or if the node is insertable for the user, or only available internally in the URCap, e.g. for use in templates.
Example:
configuration.setChildrenAllowed(false);
configuration.setUserInsertable(false);
getTitle()
Arguments: Locale locale
Returns: String
Called when: Once after registration of the Service
The getTitle-method in the SwingProgramNodeService should return the name of the ProgramNode, as shown in the Structure-overview. It is a static name, that should tell the user what this node is used for.
The localization of the system is provided as an argument, and can be used to return a node name localized to the language of the system.
Example:
if(“de”.equals(locale.getLanguage())) {
return “Greifer Öffnen”;
} else {
return “Gripper Open”;
}
createView()
Arguments: ViewAPIProvider apiProvider
Returns: V (a generic type of the SwingProgramNodeView)
Called when: One time only, first time this type of node is inserted by user or loaded in a program
The createView()-method is called only the first time the node is used. This method call should return a new instance of the View-class. The ViewAPIProvider is given as an argument. The ViewAPIProvider will typically be forwarded to the View-class, as it is required for e.g. creating keyboards on textfields.
The ViewAPIProvider can also be used to get the SystemAPI, which can be used to check the software version of the robot. This is useful to create a versioned user experience, based on whether the URCap is installed on an e-Series robot (PolyScope 5.x+) or a CB3 robot (PolyScope 3.3+).
Example:
return new MyProgramNodeView(apiProvider);
createNode()
Arguments: ProgramAPIProvider apiProvider, V view, DataModel model, CreationContext context
Returns: C (a generic type of the ProgramNodeContribution)
Called when: Whenever this type of node is created, either by the user inserting a new one, by loading a program containing this type of node, or if inserted in the program by another URCap
The createNode()-method should return a new instance of the Contribution. While there is only a singular instance of the View, there will be a new instance of the Contribution every time a new node is needed.
The arguments should as a rule of thumb be forwarded to the Contribution class, to use at its disposal.
The ProgramAPIProvider gives the contribution access to PolyScope API’s that are relevant for program nodes. The V-argument is the returned object of the createView()-method, and allows the Contribution to interact with the View.
The DataModel is what the Contribution can use, to store the settings and configuration of the node, done by the user. If the node is newly inserted, the DataModel will be blank, but if the node is loaded (e.g. as part of a program) the DataModel may already contain the configuration data for the node.
The CreationContext can be used by the Contribution to check whether the creation of itself is a result of a newly inserted node, or whether the node is loaded.
Example:
return new MyProgramNodeContribution(apiProvider, view, model, context);
/Swing Program Node View
The SwingProgramNodeView-interface dictates that a View-class must implement the following method:
buildUI()
Arguments: JPanel panel, ContributionProvider<C> provider
Returns: void
Called when: One time only, first time this type of node is inserted by user or loaded in a program
When a View is created, the buildUI()-method is called. The method provides a JPanel as an argument. This JPanel is the panel that will be shown to the user in PolyScope. The JPanel has been given the dimensions, of the actual space available in PolyScope, and during the buildUI()-call, this panel should be populated with the UI elements desired to display to the user. Upon the return of this call, the provided JPanel will be shown to the user.
Another argument is the ContributionProvider. Since there can be multiple instances of the Contribution, but only 1 instance of View, the ContributionProvider will provide access to the active Contribution, when the user interacts with the node.
Calling “provider.get()” will return the instance of the active ProgramNodeContribution.
/Program Node Contribution
A new instance of the ProgramNodeContribution is returned every time the user inserts a new node, or an existing node is loaded. The ProgramNodeContribution-interface dictates a number of methods to be implemented. An interesting point to note is, that the ProgramNodeContribution-interface is unchanged regardless of the UI being Swing or HTML based, however the way of interacting with either implementation may vary.
openView()
Arguments: void
Returns: void
Called when: Every time the user enters the user interface of this particular node
Since there is only 1 instance of the user interface, but there may be multiple instances of the ProgramNodeContribution, the openView()-method should be used to populate the user interface with the specific settings of this node.
Typically this call, can be used to read the active values in the DataModel, and update these values to be active in the UI.
This method can also be used to e.g. start a timer, which dynamically will update values in the UI, as long as this node is in focus.
closeView()
Arguments: void
Returns: void
Called when: Every time the user leaves the user interface of this particular node
The call to closeView() notifies the Contribution, that the user is now no longer viewing this node.
If a timer or other running tasks were started in openView(), it is now the time to stop these again.
isDefined()
Arguments: void
Returns: Boolean
Called when: Every time anything changes anywhere in the program (!)
isDefined() is used to signal to the user whether all necessary data have been configured, for this node to be functional. If the node has been configured to the minimal data to be able to generate script, this should return true. If data to generate the URScript is missing, this should return false.
If false is returned, the user is not able to start the program.
NOTICE:
isDefined() is called almost anytime anything changes anywhere in the program. This means that this call will happen a lot of times, even if this node is not in focus.
Therefore, the code executed in this callback must be very lean, and should ONLY DEPEND on DataModel values.
NOTICE:
It is NOT the purpose of this call, to signal whether external hardware is connected or not. This should be handled at runtime. The use of isDefined() should only have the purpose of telling the user, whether he needs to configure more data in the node or not.
getTitle()
Arguments: void
Returns: String
Called when: Frequent, typically when the DataModel is altered
The getTitle()-method in the Contribution returns the name of the program node, as shown in the program tree. Where the getTitle()-method in the Service returns the static name of the node in the list of available nodes, the getTitle()-call in the Contribution illustrates the name of the node in the program tree, typically varying based on the nodes configuration.
Typically this call will be used to change the name of the node, based on the current configuration. This is used to ease the navigation in the program tree for the user. E.g. if this is a program node for an adaptive gripper, which is set to a specific position, this could return “Gripper Move: 35 mm”, when set to a position of 35 mm. This helps the user find the appropriate node to configure. If the position was not set, and hence the node was undefined, the title could just be “Gripper Move”.
generateScript()
Arguments: ScriptWriter writer
Returns: void
Called when: Typically when the program is started or saved
Ultimately, the purpose of a Program Node is to generate some URScript, which will execute when the program is playing. The generateScript()-method is where this URScript should be generated.
Typically, the generated script, should only depend on settings from the DataModel or static values.
The script should be created using the ScriptWriter-object. E.g. by appending script lines to the ScriptWriter as “writer.appendLine(“set_digital_out(0, True)”);” or using the custom URScript calls “writer.assign(“myVariable”, “42”)”. Refer to the URScript Manual for the syntax of URScript.
The URScript generated by generateScript() is appended to the overall program, at the relative location of the node in the program.
/Principle of URCaps Installation Node integration in PolyScope
You can find a general overview of how URCaps integrate into PolyScope in this article.
In the below illustration, some of the key methods required in respectively the Service, View (UI) and Contribution are shown.
/Swing Installation Node Service
When creating an Installation Node, the SwingInstallationNodeService-interface dictates, that the following methods must be implemented:
getTitle()
Arguments: Locale locale
Returns: String
Called when: Once after registration of the Service
The getTitle-method in the SwingInstallationNodeService should return the name of the InstallationNode, as shown in the Installation-tab. It is a static name, that should tell the user what this node is used for.
The localization of the system is provided as an argument, and can be used to return a node name localized to the language of the system.
Example:
return “My Installation”;
configureContribution()
Arguments: ContributionConfiguration configuration
Returns: void
Called when: Once after registration of the Service
The ContributionConfiguration represents the configuration of this kind of installation node.
It is implemented for future use, and does currently not offer any options. It should be left blank.
createView()
Arguments: ViewAPIProvider apiProvider
Returns: V (a generic type of the SwingInstallationNodeView)
Called when: Once after registration of the Service
While a program node may exist in multiple instances at the same time, the relationship between SwingInstallationNodeView and InstallationNodeContribution is always 1:1. There will only be exactly one View-instance and exactly one Contribution-instance at the same time.
Since the Installation Node will always be a part of a loaded installation, the createView()-method is called shortly after the service is registered. This method call should return a new instance of the View-class. The ViewAPIProvider is given as an argument. The ViewAPIProvider will typically be forwarded to the View-class, as it is required for e.g. creating keyboards on textfields.
The ViewAPIProvider can also be used to get the SystemAPI, which can be used to check the software version of the robot. This is useful to create a versioned user experience, based on whether the URCap is installed on an e-Series robot (PolyScope 5.x+) or a CB3 robot (PolyScope 3.3+).
Example:
return new MyInstallationNodeView(apiProvider);
createInstallationNode()
Arguments: InstallationAPIProvider apiProvider, V view, DataModel model, CreationContext context
Returns: C (a generic type of the InstallationNodeContribution)
Called when: When the node is automatically created when PolyScope starts, or if the Installation is switched (by creating a new or loading an existing)
The createNode()-method should return a new instance of the Contribution. While there is only a singular instance of the View, a new instance of the Installation Node Contribution may be created, when the Installation on the robot is switched. This can happen if a new installation is created, or an existing installation file is loaded.
The arguments should as a rule of thumb be forwarded to the Contribution class, to use at its disposal.
The InstallationAPIProvider gives the contribution access to PolyScope API’s that are relevant for installation nodes. The V-argument is the returned object of the createView()-method, and allows the Contribution to interact with the View.
The DataModel is what the Contribution can use, to store the settings and configuration of the node, done by the user. If the node is newly inserted, the DataModel will be blank, but if the node is loaded (e.g. as part of an installation file) the DataModel may already contain the configuration data for the node.
The CreationContext can be used by the Contribution to check whether the creation of itself is a result of a newly inserted node, or whether the node is loaded.
Example:
return new MyInstallationNodeContribution(apiProvider, view, model, context);
/Swing Installation Node View
The SwingInstallationNodeView-interface dictates that a View-class must implement the following method:
buildUI()
Arguments: JPanel panel, C contribution (a generic type of the InstallationNodeContribution)
Returns: void
Called when: Once after registration of the Service
When a View is created, the buildUI()-method is called. The method provides a JPanel as an argument. This JPanel is the panel that will be shown to the user in PolyScope. The JPanel has been given the dimensions, of the actual space available in PolyScope, and during the buildUI()-call, this panel should be populated with the UI elements desired to display to the user. Upon the return of this call, the provided JPanel will be shown to the user.
The loaded instance of the InstallationNodeContribution is provided as an argument, and the View-class may use this to interact with the Contribution, when the user performs actions in the UI.
/Installation Node Contribution
The InstallationNodeContribution-interface dictates a number of methods to be implemented. An interesting point to note is, that the InstallationNodeContribution-interface is unchanged regardless of the UI being Swing or HTML based, however the way of interacting with either implementation may vary.
openView()
Arguments: void
Returns: void
Called when: Every time the user enters the user interface of this particular node
The openView()-method should be used to populate the user interface with the active settings of the node. Typically this call, can be used to read the active values in the DataModel, and update these values to be active in the UI.
This method can also be used to e.g. start a timer, which dynamically will update values in the UI, as long as this node is in focus.
closeView()
Arguments: void
Returns: void
Called when: Every time the user leaves the user interface of this particular node
The call to closeView() notifies the Contribution, that the user is now no longer viewing this node.
If a timer or other running tasks were started in openView(), it is now the time to stop these again.
generateScript()
Arguments: ScriptWriter writer
Returns: void
Called when: Typically when the program is started or saved
When an Installation Node is contributing URScript to the overall program, this will be used as a preamble before the program node logic and URScript is added.
This makes the Installation Node useful for initializing values or settings, or defining functions used later in the program by a program node.
Typically, the generated script, should only depend on settings from the DataModel or static values.
The script should be created using the ScriptWriter-object. E.g. by appending script lines to the ScriptWriter as “writer.appendLine(“set_digital_out(0, True)”);” or using the custom URScript calls “writer.assign(“myVariable”, “42”)”. Refer to the URScript Manual for the syntax of URScript.
/Principle of URCaps Toolbar integration in PolyScope
You can find a general overview of how URCaps integrate into PolyScope in this article.
In the below illustration, some of the key methods required in respectively the Service, View (UI) and Contribution are shown. The toolbar differs from the Program Node and Installation Node, as it does not have any context to either a program or an installation, but is rather global for the robot. The Toolbar is mainly used for live control of external devices, or displaying runtime data to the use.
/Swing Toolbar Service
The SwingToolbarService-interface requires a number of methods to be implemented:
getIcon()
Arguments: void
Returns: Icon
Called when: Once after registration of the Service
The user navigates between the active toolbars on the robot, by clicking the Toolbars icon. The particular Icon for this Toolbar should be returned in the getIcon()-method.
Refer to the Toolbar Contributions guide in the SDK for restrictions on size etc.
Example:
return new ImageIcon(getClass().getResource("/icons/myToolbarIcon.png"));
configureContribution()
Arguments: ToolbarConfiguration configuration
Returns: void
Called when: Once after registration of the Service
The ToolbarConfiguration represents the configuration of toolbar. The ToolbarConfiguration -object can be altered, by calling the associated methods.
The configuration can be used to define the height of the toolbar. This value is static, and can only be set one time. If the value provided here is within range, the JPanel later handed to the SwingToolbarContribution will have this dimension.
Example:
configuration.setToolbarHeight(200);
createToolbar()
Arguments: ToolbarContext context
Returns: SwingToolbarContribution
Called when: Once after registration of the Service
When createToolbar() is called, this should return a new instance of the SwingToolbarContribution.
The argument ToolbarContext should be forwarded to the Contribution, as this may later be used to access relevant API’s and services for a toolbar.
/Swing Toolbar Contribution
As the Toolbar does not have a context to the program or installation domains of the robot, the SwingToolbarContribution serves as both the logical code and the UI, which would be two separate classes for e.g. a Program Node. The following methods must be implemented by the SwingToolbarContribution:
buildUI()
Arguments: JPanel panel
Returns: void
Called when: Once after registration of the Service
This method provides a JPanel as an argument. This JPanel is the panel that will be shown to the user in PolyScope. The JPanel has been given the dimensions, of the actual space available in PolyScope, and during the buildUI()-call, this panel should be populated with the UI elements desired to display to the user.
openView()
Arguments: void
Returns: void
Called when: Every time the user opens this toolbar
The openView()-call can be used to e.g. start a timer, which dynamically can update values in the Toolbar.
closeView()
Arguments: void
Returns: void
Called when: Every time the user closes this toolbar
If a timer or another similar process was started in openView(), this should now be terminated in closeView().