URCap Button to run URScript

Hi,

I am new in URCap Development and I have a URCap implemented with Swing that generates a USscript when running the program, but I also want to run another two URScript to change a digital output when pushing two buttons in the Program Tab.

The code snippet of the ProgramNodeView is the following:

Where the actionperformed event of each button should call a function to generate script in the ProgramContributionNode like this (in this case they are only showing in the View some labels with random numbers)

image
image

So I want to know what should I change in both ViewNode and ContributionNode functions to reach this objective.

Thank you in advanced.

Check out this link, and add these class files to your project. They give you functions for sending lines of script for execution from java. Once you have those, something like the following code will, in this case, set digital output 1 to True:

				final ScriptCommand script = new ScriptCommand();
				//create a new String to hold the command
				String string = "set_digital_out(1,True)";
				//assign the newly created command as Primary. Secondary commands cannot consume robot time
				script.setAsPrimaryProgram();
				//add the created string to the command
				script.appendLine(string);
				//create a new instance of a ScriptSender
				final ScriptSender sender = new ScriptSender();
				//send the created script using the newly created sender
				sender.sendScriptCommand(script);

You could replace .setAsPrimaryProgram() with .setAsSecondaryProgram() if you don’t want your main program to be interrupted.

Now, that’s fine for sending script commands. But I’ll point out, it’s kinda laggy, and if you spam those buttons, you can really lag the robot. If all you’re trying to do is set a physical output via Java, that’s possible to do directly and much quicker.

import java.util.Collection;
import java.util.Iterator;

import com.ur.urcap.api.domain.io.DigitalIO;
import com.ur.urcap.api.domain.io.IO;
import com.ur.urcap.api.domain.io.IOModel;
import com.ur.urcap.api.domain.io.ModbusIO;

public class IOHelper {

	private final IOModel ioModel;

	public IOHelper(final IOModel ioModel) {
		this.ioModel = ioModel;
	}

	public DigitalIO[] getDigitalInputs() {
		final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
		ioCollection.removeIf(n -> !n.isInput());
		final int inputSize = ioCollection.size();
		final Iterator<DigitalIO> itr = ioCollection.iterator();
		final DigitalIO[] inputList = new DigitalIO[inputSize];

		for (int i = 0; i < inputSize; i++) {
			inputList[i] = itr.next();
		}
		return inputList;
	}
	
	
	
	public DigitalIO[] getDigitalOutputs() {
		final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
		ioCollection.removeIf(DigitalIO::isInput);
		final int outputSize = ioCollection.size();
		final Iterator<DigitalIO> itr = ioCollection.iterator();
		final DigitalIO[] outputList = new DigitalIO[outputSize];

		for (int i = 0; i < outputSize; i++) {
			outputList[i] = itr.next();
		}
		return outputList;
	}

	public ModbusIO[] getModBusInputs() {
		final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
		ioCollection.removeIf(n -> !n.isInput());
		final int inputSize = ioCollection.size();
		final Iterator<ModbusIO> itr = ioCollection.iterator();
		final ModbusIO[] inputList = new ModbusIO[inputSize];

		for (int i = 0; i < inputSize; i++) {
			inputList[i] = itr.next();
		}
		return inputList;
	}

	public ModbusIO[] getModBusOutputs() {
		final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
		ioCollection.removeIf(ModbusIO::isInput);
		final int outputSize = ioCollection.size();
		final Iterator<ModbusIO> itr = ioCollection.iterator();
		final ModbusIO[] outputList = new ModbusIO[outputSize];

		for (int i = 0; i < outputSize; i++) {
			outputList[i] = itr.next();
		}
		return outputList;
	}

	public DigitalIO getSpecificDigitalIO(final String defaultName) {
		final Collection<DigitalIO> ioCollection = ioModel.getIOs(DigitalIO.class);
		final int ioCount = ioCollection.size();

		if (ioCount > 0) {
			final Iterator<DigitalIO> ioItr = ioCollection.iterator();
			while (ioItr.hasNext()) {
				final DigitalIO thisIO = ioItr.next();
				final String thisDefaultName = thisIO.getDefaultName();
				if (thisDefaultName.equals(defaultName)) {
					return thisIO;
				}
			}
		}
		return null;
	}
	
	public IO getSpecificGPIO(final String defaultName) {
		final Collection<IO> ioCollection = ioModel.getIOs();
		final int ioCount = ioCollection.size();

		if (ioCount > 0) {
			final Iterator<IO> ioItr = ioCollection.iterator();
			while (ioItr.hasNext()) {
				final IO thisIO = ioItr.next();
				final String thisDefaultName = thisIO.getDefaultName();
				if (thisDefaultName.equals(defaultName)) {
					return thisIO;
				}
			}
		}
		return null;
	}
	
	

	public ModbusIO getSpecificModBusIO(final String signalName) {
		final Collection<ModbusIO> ioCollection = ioModel.getIOs(ModbusIO.class);
		final int ioCount = ioCollection.size();
		if (ioCount > 0) {
			final Iterator<ModbusIO> ioItr = ioCollection.iterator();
			while (ioItr.hasNext()) {
				final ModbusIO thisIO = ioItr.next();
				final String thisSignalAddress = thisIO.getName();
				if (thisSignalAddress.equals(signalName)) {
					return thisIO;
				}
			}
		}
		return null;
	}

}

Here’s the whole classfile for IOHelper, which just makes it a little easier to access the IO you want to control.
I reference this link when I need to know the name of an IO: How to obtain a list of ALL the IOs on the robot using the URCap Java API - #3 by fujikit

Once you have that, you can just say something like:

DigitalIO digitalOut1= IOHelper.getSpecificDigitalIO("digital_out[1]");

Now in the action of your button, you can do:

digitalOut1.setValue(true);

to turn it on, and setValue(false) to turn it off.

Hope some of that is helpful.

1 Like

Thanks Eric,

The first way worked for me but it is true that spamming the button produces lag in the robot. However, the second way when I add IOHelper to my project and say DigitalIO digitalOut1= IOHelper.getSpecificDigitalIO(“digital_out[1]”); it returns to me the following error tip:

Cannot make a static reference to the non-static method getSpecificDigitalIO(String) from the type IOHelper

And it suggest me to change the getSpecificDigitalIO method to static and it doesn’t work either.

Do you know which could be the problem?

Thanks for your help.

Ahh oops. I steered you wrong a little bit there. In case you see this type of error later, it is because you are making a reference to the actual class itself, instead of an instance of the class. IE by typing IOHelper.blahBlahBlah you’re trying to statically access a class that is not static.

To fix this, create an instance of IOHelper at the top of your contribution/view/whatever and access that instead.

private final IOHelper ioHelper; //Instance declaration
ioHelper = new IOHelper(context.getAPIProvider().getApplicationAPI().getIOModel()); //initialization

In this example, I use this from the toolbar, which is why I use the context as opposed to the apiProvider directly. You’ll want to use whatever can get you the getIOModel() method.

Thanks Eric again,

this time the second way worked for me and I can now set the digital outputs without lagging the robot.

I am very grateful for your help, thank you very much!

Hi,
I am trying to do something similar.
I was able to implement the IOHelper for a Toolbar. But right know I can not get it to work for an InstallationNode.

In this example, I use this from the toolbar, which is why I use the context as opposed to the apiProvider directly. You’ll want to use whatever can get you the getIOModel() method.

I tried to do that but could not get it right.
Are you able to show me how you got around this problem?

In the constructor for the Installation node, you should be able to say:

apiProvider.getInstallationAPI().getIOModel();

Thanks for your response.
That’s the thing I tried but did not work. Eclipse returns the error message:

Preformatted textThe method getInstallationAPI() is undefined for the type ViewAPIProvider

Eclipse then suggests to

Add cast to ‘apiProvider’

which of course does not work. Maybe I did a failure elsewhere but I am not able to find it.

I just commented everything not needed and modified my code so that eclipse will let me run it. You might see that ioModel is always null, so my code wont work as intended:

View Class:

package com.ruhrboticsGmbH.ProfinetMonitor.impl;

import java.awt.*;
import javax.swing.*;

import com.ur.urcap.api.contribution.installation.swing.SwingInstallationNodeView;
import com.ur.urcap.api.contribution.ViewAPIProvider;

import com.ur.urcap.api.domain.io.DigitalIO;
import com.ur.urcap.api.domain.io.IOModel;

public class ProfinetMonitorInstallationNodeView implements SwingInstallationNodeView<ProfinetMonitorInstallationNodeContribution> {

    private final ViewAPIProvider apiProvider;
    private final JTabbedPane tabbedPane = new JTabbedPane();

    private IOHelper ioHelper = null;
    private final IOModel ioModel;
    
    private DigitalIO[] digitalOutputs;
    private String[] digitalOutputNames;

    public ProfinetMonitorInstallationNodeView(ViewAPIProvider apiProvider, IOModel ioModel) {
        
		this.apiProvider = apiProvider;
		this.ioModel = ioModel;
		this.ioHelper = ioHelper;
    }

    @Override
    public void buildUI(JPanel panel, ProfinetMonitorInstallationNodeContribution contribution) {	
        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
        tabbedPane.addTab("Digital", createPanelDigital());
        panel.add(tabbedPane);
    }

    private JPanel createPanelDigital() {
        JPanel[] inputOutputPanels = createInputOutputPanels();

        JScrollPane scrollPaneInputs = createDigitalOutputScrollPane();
        JScrollPane scrollPaneOutputs = createDigitalOutputScrollPane();

        addInputOutputComponents(inputOutputPanels, scrollPaneInputs, scrollPaneOutputs);

        return createMainPanelStructure(inputOutputPanels);
    }

    private void addInputOutputComponents(JPanel[] inputOutputPanels, JScrollPane scrollPaneInputs, JScrollPane scrollPaneOutputs) {
        Box boxInputsHeadline = createLabeledBox(new JLabel(" Inputs"));
        Box boxInputs = createBoxWithComponent(scrollPaneInputs);
        Box boxOutputsHeadline = createLabeledBox(new JLabel(" Outputs"));
        Box boxOutputs = createBoxWithComponent(scrollPaneOutputs);

        inputOutputPanels[0].add(boxInputsHeadline);
        inputOutputPanels[0].add(boxInputs);
        inputOutputPanels[1].add(boxOutputsHeadline);
        inputOutputPanels[1].add(boxOutputs);
    }

    private JPanel createMainPanelStructure(JPanel[] inputOutputPanels) {
        JPanel primaryPanel = new JPanel(new BorderLayout());
        JPanel secondaryCenterPanel = new JPanel();
        secondaryCenterPanel.setLayout(new BoxLayout(secondaryCenterPanel, BoxLayout.LINE_AXIS));
        
        secondaryCenterPanel.add(inputOutputPanels[0]);
        secondaryCenterPanel.add(inputOutputPanels[1]);
        
        primaryPanel.add(secondaryCenterPanel, BorderLayout.CENTER);
        primaryPanel.add(createImagePanel(), BorderLayout.SOUTH);
        return primaryPanel;
    }

    private JPanel[] createInputOutputPanels() {
        JPanel panelS1 = new JPanel();
        panelS1.setLayout(new BoxLayout(panelS1, BoxLayout.PAGE_AXIS));
        panelS1.setBorder(BorderFactory.createLineBorder(Color.decode("#45494C")));
        panelS1.setBackground(Color.decode("#FF9E1B"));

        JPanel panelS2 = new JPanel();
        panelS2.setLayout(new BoxLayout(panelS2, BoxLayout.PAGE_AXIS));
        panelS2.setBorder(BorderFactory.createLineBorder(Color.decode("#45494C")));
        panelS2.setBackground(Color.decode("#FF9E1B"));

        return new JPanel[]{panelS1, panelS2};
    }

    private JScrollPane createDigitalOutputScrollPane() {
    	
    	digitalOutputs = new DigitalIO[8];
    	digitalOutputNames = new String[8];
    	
    	JPanel panel = new JPanel();
    	panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    	
    	for (int i = 0; i < 8; i++) {
    		if (digitalOutputs[i] != null) {
    			digitalOutputNames[i] = digitalOutputs[i].getName();
    		}
    		else {
    			digitalOutputNames[i] = "Output " + i;
    		}
    		JLabel label = new JLabel(digitalOutputNames[i]);
    		panel.add(label);
    	}

		JScrollPane OutputList = new JScrollPane(panel);
    	
    	
    	return OutputList;
    }

    private Box createLabeledBox(JLabel label) {
        label.setFont(new Font("SansSerif", Font.BOLD, 18));
        Box box = Box.createHorizontalBox();
        box.add(label);
        return box;
    }

    private Box createBoxWithComponent(Component component) {
        Box box = Box.createHorizontalBox();
        box.add(component);
        return box;
    }

    private JPanel createImagePanel() {
        JPanel imagePanel = new JPanel();
        imagePanel.setLayout(new BoxLayout(imagePanel, BoxLayout.X_AXIS));
        ImageIcon logoIcon = new ImageIcon(getClass().getResource("/icons/ruhrbotics_logo_lang.png"));
        JLabel logoLabel = new JLabel(logoIcon);
        imagePanel.add(Box.createHorizontalGlue());
        imagePanel.add(logoLabel);
        return imagePanel;
    }
}

Contribution Class:

package com.ruhrboticsGmbH.ProfinetMonitor.impl;

import com.ur.urcap.api.contribution.InstallationNodeContribution;
import com.ur.urcap.api.contribution.installation.InstallationAPIProvider;
import com.ur.urcap.api.domain.data.DataModel;
import com.ur.urcap.api.domain.script.ScriptWriter;

import com.ur.urcap.api.domain.io.IOModel;

public class ProfinetMonitorInstallationNodeContribution implements InstallationNodeContribution {
	
	private final InstallationAPIProvider apiProvider;
	private final ProfinetMonitorInstallationNodeView view;
	private final DataModel model;
	
	private final IOHelper ioHelper;
	
	public ProfinetMonitorInstallationNodeContribution(InstallationAPIProvider apiProvider, IOModel ioModel, ProfinetMonitorInstallationNodeView view,
			DataModel model) {
		this.apiProvider = apiProvider;
		this.view = view;
		
		this.ioHelper = new IOHelper(ioModel);
		
		this.model = model;
	}
	
	@Override
	public void openView() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void closeView() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void generateScript(ScriptWriter writer) {
		// TODO Auto-generated method stub
		
	}

}

Service Class:

package com.ruhrboticsGmbH.ProfinetMonitor.impl;

import java.util.Locale;

import com.ur.urcap.api.contribution.ViewAPIProvider;
import com.ur.urcap.api.contribution.installation.ContributionConfiguration;
import com.ur.urcap.api.contribution.installation.CreationContext;
import com.ur.urcap.api.contribution.installation.InstallationAPIProvider;
import com.ur.urcap.api.contribution.installation.swing.SwingInstallationNodeService;
import com.ur.urcap.api.domain.data.DataModel;

import com.ur.urcap.api.domain.io.IOModel;

public class ProfinetMonitorInstallationNodeService implements SwingInstallationNodeService<ProfinetMonitorInstallationNodeContribution, ProfinetMonitorInstallationNodeView>{
	
	@Override
	public void configureContribution(ContributionConfiguration configuration) {
		
	}

	@Override
	public String getTitle(Locale locale) {
		return "Profinet";
	}

	@Override
	public ProfinetMonitorInstallationNodeView createView(ViewAPIProvider apiProvider) {
		
		IOModel ioModel = null;
		
		return new ProfinetMonitorInstallationNodeView(apiProvider, ioModel);
	}

	@Override
	public ProfinetMonitorInstallationNodeContribution createInstallationNode(InstallationAPIProvider apiProvider,
			ProfinetMonitorInstallationNodeView view, DataModel model, CreationContext context) {
		
		IOModel ioModel = null;
		
		return new ProfinetMonitorInstallationNodeContribution(apiProvider, ioModel, view, model);
	}

}

When I change everything to how I think it should work my view class looks like this:

Okay…to finish this topic I was able to solve my problems

The Problem was that getIOModel() is no part of the ViewAPIProvider.
How did I solve this problem?

I accessed the instance of IOHelper in the InstallationNodeService:

	public ProfinetMonitorInstallationNodeContribution createInstallationNode(InstallationAPIProvider apiProvider,
			ProfinetMonitorInstallationNodeView view, DataModel model, CreationContext context) {
		
		IOModel ioModel = apiProvider.getInstallationAPI().getIOModel();
		
		return new ProfinetMonitorInstallationNodeContribution(apiProvider, view, model, ioModel);
	}

Then I refference the IOHelper in der InstallationNodeContribution and define a new method that I write into the InstallationNodeView:

InstallationNodeContribution:

	private final IOHelper ioHelper;
	
	public ProfinetMonitorInstallationNodeContribution(InstallationAPIProvider apiProvider, ProfinetMonitorInstallationNodeView view,
			DataModel model, IOModel ioModel) {
		this.apiProvider = apiProvider;
		this.view = view;
		this.model = model;
		
		this.ioHelper = new IOHelper(ioModel);
		
		view.setIOHelper(ioHelper);
	}

InstallationNodeView:

    public void setIOHelper(IOHelper ioHelper) {
    	this.ioHelper = ioHelper;
    }

When you have questions to my solution please do not hesitate to reply.

Yeah so I was going to say just put it in the contribution not the view. You don’t even need to go as high as the Service class, because the Service already constructs the contribution with the InstallationAPIProvider, which is exactly what you’re using in the Service. Then yeah you either pass the reference to the IOHelper into the View, or let the View call methods out of the contribution when necessary (which I think is what UR’s guidelines recommend but it doesn’t really matter)