/*
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* OpenPnP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/
package org.openpnp.gui;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.FocusTraversalPolicy;
import java.awt.Font;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openpnp.ConfigurationListener;
import org.openpnp.gui.support.Icons;
import org.openpnp.model.Configuration;
import org.openpnp.model.Length;
import org.openpnp.model.LengthUnit;
import org.openpnp.model.Location;
import org.openpnp.spi.Actuator;
import org.openpnp.spi.Head;
import org.openpnp.spi.HeadMountable;
import org.openpnp.spi.Machine;
import org.openpnp.spi.Nozzle;
import org.openpnp.spi.PasteDispenser;
import org.openpnp.util.BeanUtils;
import org.openpnp.util.MovableUtils;
import org.openpnp.util.UiUtils;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.FormSpecs;
import com.jgoodies.forms.layout.RowSpec;
/**
* Contains controls, DROs and status for the machine. Controls: C right / left, X + / -, Y + / -, Z
* + / -, stop, pause, slider for jog increment DROs: X, Y, Z, C Radio buttons to select mm or inch.
*
* @author jason
*/
public class JogControlsPanel extends JPanel {
private final MachineControlsPanel machineControlsPanel;
private final Configuration configuration;
private JPanel panelActuators;
private JPanel panelDispensers;
private JSlider sliderIncrements;
/**
* Create the panel.
*/
public JogControlsPanel(Configuration configuration, MachineControlsPanel machineControlsPanel) {
this.machineControlsPanel = machineControlsPanel;
this.configuration = configuration;
createUi();
configuration.addListener(configurationListener);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
xPlusAction.setEnabled(enabled);
xMinusAction.setEnabled(enabled);
yPlusAction.setEnabled(enabled);
yMinusAction.setEnabled(enabled);
zPlusAction.setEnabled(enabled);
zMinusAction.setEnabled(enabled);
cPlusAction.setEnabled(enabled);
cMinusAction.setEnabled(enabled);
discardAction.setEnabled(enabled);
safezAction.setEnabled(enabled);
xyParkAction.setEnabled(enabled);
zParkAction.setEnabled(enabled);
cParkAction.setEnabled(enabled);
for (Component c : panelActuators.getComponents()) {
c.setEnabled(enabled);
}
for (Component c : panelDispensers.getComponents()) {
c.setEnabled(enabled);
}
}
private void setUnits(LengthUnit units) {
if (units == LengthUnit.Millimeters) {
Hashtable<Integer, JLabel> incrementsLabels = new Hashtable<>();
incrementsLabels.put(1, new JLabel("0.01 " + units.getShortName()));
incrementsLabels.put(2, new JLabel("0.1 " + units.getShortName()));
incrementsLabels.put(3, new JLabel("1.0 " + units.getShortName()));
incrementsLabels.put(4, new JLabel("10 " + units.getShortName()));
incrementsLabels.put(5, new JLabel("100 " + units.getShortName()));
sliderIncrements.setLabelTable(incrementsLabels);
}
else if (units == LengthUnit.Inches) {
Hashtable<Integer, JLabel> incrementsLabels = new Hashtable<>();
incrementsLabels.put(1, new JLabel("0.001 " + units.getShortName()));
incrementsLabels.put(2, new JLabel("0.01 " + units.getShortName()));
incrementsLabels.put(3, new JLabel("0.1 " + units.getShortName()));
incrementsLabels.put(4, new JLabel("1.0 " + units.getShortName()));
incrementsLabels.put(5, new JLabel("10.0 " + units.getShortName()));
sliderIncrements.setLabelTable(incrementsLabels);
}
else {
throw new Error("setUnits() not implemented for " + units);
}
machineControlsPanel.updateDros();
}
public double getJogIncrement() {
if (configuration.getSystemUnits() == LengthUnit.Millimeters) {
return 0.01 * Math.pow(10, sliderIncrements.getValue() - 1);
}
else if (configuration.getSystemUnits() == LengthUnit.Inches) {
return 0.001 * Math.pow(10, sliderIncrements.getValue() - 1);
}
else {
throw new Error(
"getJogIncrement() not implemented for " + configuration.getSystemUnits());
}
}
private void jog(final int x, final int y, final int z, final int c) {
UiUtils.submitUiMachineTask(() -> {
Location l = machineControlsPanel.getSelectedNozzle().getLocation()
.convertToUnits(Configuration.get().getSystemUnits());
double xPos = l.getX();
double yPos = l.getY();
double zPos = l.getZ();
double cPos = l.getRotation();
double jogIncrement =
new Length(getJogIncrement(), configuration.getSystemUnits()).getValue();
if (x > 0) {
xPos += jogIncrement;
}
else if (x < 0) {
xPos -= jogIncrement;
}
if (y > 0) {
yPos += jogIncrement;
}
else if (y < 0) {
yPos -= jogIncrement;
}
if (z > 0) {
zPos += jogIncrement;
}
else if (z < 0) {
zPos -= jogIncrement;
}
if (c > 0) {
cPos += jogIncrement;
}
else if (c < 0) {
cPos -= jogIncrement;
}
machineControlsPanel.getSelectedNozzle()
.moveTo(new Location(l.getUnits(), xPos, yPos, zPos, cPos));
});
}
private void park(boolean xy, boolean z, boolean c) {
UiUtils.submitUiMachineTask(() -> {
HeadMountable tool = machineControlsPanel.getSelectedTool();
Location location = tool.getLocation();
Location parkLocation = tool.getHead().getParkLocation();
parkLocation = parkLocation.convertToUnits(location.getUnits());
location = location.derive(xy ? parkLocation.getX() : null,
xy ? parkLocation.getY() : null, z ? parkLocation.getZ() : null,
c ? parkLocation.getRotation() : null);
MovableUtils.moveToLocationAtSafeZ(tool, location);
});
}
private void createUi() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setFocusTraversalPolicy(focusPolicy);
setFocusTraversalPolicyProvider(true);
JTabbedPane tabbedPane_1 = new JTabbedPane(JTabbedPane.TOP);
add(tabbedPane_1);
JPanel panelControls = new JPanel();
tabbedPane_1.addTab("Jog", null, panelControls, null);
panelControls.setLayout(new FormLayout(
new ColumnSpec[] {FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,
FormSpecs.RELATED_GAP_COLSPEC, ColumnSpec.decode("default:grow"),},
new RowSpec[] {FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,
FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,
FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,
FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,
FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,
FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,}));
JButton homeButton = new JButton(machineControlsPanel.homeAction);
// We set this Icon explicitly as a WindowBuilder helper. WindowBuilder can't find the
// homeAction referenced above so the icon doesn't render in the viewer. We set it here
// so the dialog looks right while editing.
homeButton.setIcon(Icons.home);
homeButton.setHideActionText(true);
panelControls.add(homeButton, "2, 2");
JLabel lblXy = new JLabel("X/Y");
lblXy.setFont(new Font("Lucida Grande", Font.PLAIN, 22));
lblXy.setHorizontalAlignment(SwingConstants.CENTER);
panelControls.add(lblXy, "8, 2, fill, default");
JLabel lblZ = new JLabel("Z");
lblZ.setHorizontalAlignment(SwingConstants.CENTER);
lblZ.setFont(new Font("Lucida Grande", Font.PLAIN, 22));
panelControls.add(lblZ, "14, 2");
JLabel lblDistance = new JLabel("Distance");
lblDistance.setFont(new Font("Lucida Grande", Font.PLAIN, 10));
panelControls.add(lblDistance, "18, 2, center, center");
JLabel lblSpeed = new JLabel("Speed %");
lblSpeed.setFont(new Font("Lucida Grande", Font.PLAIN, 10));
panelControls.add(lblSpeed, "20, 2, center, center");
sliderIncrements = new JSlider();
panelControls.add(sliderIncrements, "18, 3, 1, 10");
sliderIncrements.setOrientation(SwingConstants.VERTICAL);
sliderIncrements.setMajorTickSpacing(1);
sliderIncrements.setValue(1);
sliderIncrements.setSnapToTicks(true);
sliderIncrements.setPaintLabels(true);
sliderIncrements.setMinimum(1);
sliderIncrements.setMaximum(5);
JButton yPlusButton = new JButton(yPlusAction);
yPlusButton.setHideActionText(true);
panelControls.add(yPlusButton, "8, 4");
JButton zUpButton = new JButton(zPlusAction);
zUpButton.setHideActionText(true);
panelControls.add(zUpButton, "14, 4");
speedSlider = new JSlider();
speedSlider.setValue(100);
speedSlider.setPaintTicks(true);
speedSlider.setMinorTickSpacing(1);
speedSlider.setMajorTickSpacing(25);
speedSlider.setSnapToTicks(true);
speedSlider.setPaintLabels(true);
speedSlider.setOrientation(SwingConstants.VERTICAL);
panelControls.add(speedSlider, "20, 4, 1, 9");
speedSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
Configuration.get().getMachine().setSpeed(speedSlider.getValue() * 0.01);
}
});
JButton buttonStartStop = new JButton(machineControlsPanel.startStopMachineAction);
buttonStartStop.setIcon(Icons.powerOn);
panelControls.add(buttonStartStop, "2, 6");
buttonStartStop.setHideActionText(true);
JButton xMinusButton = new JButton(xMinusAction);
xMinusButton.setHideActionText(true);
panelControls.add(xMinusButton, "6, 6");
JButton homeXyButton = new JButton(xyParkAction);
homeXyButton.setHideActionText(true);
panelControls.add(homeXyButton, "8, 6");
JButton xPlusButton = new JButton(xPlusAction);
xPlusButton.setHideActionText(true);
panelControls.add(xPlusButton, "10, 6");
JButton homeZButton = new JButton(zParkAction);
homeZButton.setHideActionText(true);
panelControls.add(homeZButton, "14, 6");
JButton yMinusButton = new JButton(yMinusAction);
yMinusButton.setHideActionText(true);
panelControls.add(yMinusButton, "8, 8");
JButton zDownButton = new JButton(zMinusAction);
zDownButton.setHideActionText(true);
panelControls.add(zDownButton, "14, 8");
JLabel lblC = new JLabel("C");
lblC.setHorizontalAlignment(SwingConstants.CENTER);
lblC.setFont(new Font("Lucida Grande", Font.PLAIN, 22));
panelControls.add(lblC, "4, 12");
JButton counterclockwiseButton = new JButton(cPlusAction);
counterclockwiseButton.setHideActionText(true);
panelControls.add(counterclockwiseButton, "6, 12");
JButton homeCButton = new JButton(cParkAction);
homeCButton.setHideActionText(true);
panelControls.add(homeCButton, "8, 12");
JButton clockwiseButton = new JButton(cMinusAction);
clockwiseButton.setHideActionText(true);
panelControls.add(clockwiseButton, "10, 12");
JPanel panelSpecial = new JPanel();
tabbedPane_1.addTab("Special", null, panelSpecial, null);
FlowLayout flowLayout_1 = (FlowLayout) panelSpecial.getLayout();
flowLayout_1.setAlignment(FlowLayout.LEFT);
JButton btnSafeZ = new JButton(safezAction);
panelSpecial.add(btnSafeZ);
JButton btnDiscard = new JButton(discardAction);
panelSpecial.add(btnDiscard);
panelActuators = new JPanel();
tabbedPane_1.addTab("Actuators", null, panelActuators, null);
panelActuators.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
panelDispensers = new JPanel();
tabbedPane_1.addTab("Dispense", null, panelDispensers, null);
FlowLayout flowLayout = (FlowLayout) panelDispensers.getLayout();
flowLayout.setAlignment(FlowLayout.LEFT);
}
private FocusTraversalPolicy focusPolicy = new FocusTraversalPolicy() {
@Override
public Component getComponentAfter(Container aContainer, Component aComponent) {
return sliderIncrements;
}
@Override
public Component getComponentBefore(Container aContainer, Component aComponent) {
return sliderIncrements;
}
@Override
public Component getDefaultComponent(Container aContainer) {
return sliderIncrements;
}
@Override
public Component getFirstComponent(Container aContainer) {
return sliderIncrements;
}
@Override
public Component getInitialComponent(Window window) {
return sliderIncrements;
}
@Override
public Component getLastComponent(Container aContainer) {
return sliderIncrements;
}
};
public double getSpeed() {
return speedSlider.getValue() * 0.01D;
}
@SuppressWarnings("serial")
public Action yPlusAction = new AbstractAction("Y+", Icons.arrowUp) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(0, 1, 0, 0);
}
};
@SuppressWarnings("serial")
public Action yMinusAction = new AbstractAction("Y-", Icons.arrowDown) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(0, -1, 0, 0);
}
};
@SuppressWarnings("serial")
public Action xPlusAction = new AbstractAction("X+", Icons.arrowRight) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(1, 0, 0, 0);
}
};
@SuppressWarnings("serial")
public Action xMinusAction = new AbstractAction("X-", Icons.arrowLeft) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(-1, 0, 0, 0);
}
};
@SuppressWarnings("serial")
public Action zPlusAction = new AbstractAction("Z+", Icons.arrowUp) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(0, 0, 1, 0);
}
};
@SuppressWarnings("serial")
public Action zMinusAction = new AbstractAction("Z-", Icons.arrowDown) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(0, 0, -1, 0);
}
};
@SuppressWarnings("serial")
public Action cPlusAction = new AbstractAction("C+", Icons.rotateCounterclockwise) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(0, 0, 0, 1);
}
};
@SuppressWarnings("serial")
public Action cMinusAction = new AbstractAction("C-", Icons.rotateClockwise) {
@Override
public void actionPerformed(ActionEvent arg0) {
jog(0, 0, 0, -1);
}
};
@SuppressWarnings("serial")
public Action xyParkAction = new AbstractAction("Park XY", Icons.park) {
@Override
public void actionPerformed(ActionEvent arg0) {
park(true, false, false);
}
};
@SuppressWarnings("serial")
public Action zParkAction = new AbstractAction("Park Z", Icons.park) {
@Override
public void actionPerformed(ActionEvent arg0) {
park(false, true, false);
}
};
@SuppressWarnings("serial")
public Action cParkAction = new AbstractAction("Park C", Icons.park) {
@Override
public void actionPerformed(ActionEvent arg0) {
park(false, false, true);
}
};
@SuppressWarnings("serial")
public Action safezAction = new AbstractAction("Head Safe Z") {
@Override
public void actionPerformed(ActionEvent arg0) {
UiUtils.submitUiMachineTask(() -> {
Configuration.get().getMachine().getDefaultHead().moveToSafeZ();
});
}
};
@SuppressWarnings("serial")
public Action discardAction = new AbstractAction("Discard") {
@Override
public void actionPerformed(ActionEvent arg0) {
UiUtils.submitUiMachineTask(() -> {
Nozzle nozzle = machineControlsPanel.getSelectedNozzle();
// move to the discard location
MovableUtils.moveToLocationAtSafeZ(nozzle,
Configuration.get().getMachine().getDiscardLocation());
// discard the part
nozzle.place();
nozzle.moveToSafeZ();
});
}
};
@SuppressWarnings("serial")
public Action raiseIncrementAction = new AbstractAction("Raise Jog Increment") {
@Override
public void actionPerformed(ActionEvent arg0) {
sliderIncrements.setValue(
Math.min(sliderIncrements.getMaximum(), sliderIncrements.getValue() + 1));
}
};
@SuppressWarnings("serial")
public Action lowerIncrementAction = new AbstractAction("Lower Jog Increment") {
@Override
public void actionPerformed(ActionEvent arg0) {
sliderIncrements.setValue(
Math.max(sliderIncrements.getMinimum(), sliderIncrements.getValue() - 1));
}
};
private void addActuator(Actuator actuator) {
String name = actuator.getHead() == null ? actuator.getName() : actuator.getHead().getName() + ":" + actuator.getName();
JToggleButton actuatorButton = new JToggleButton(name);
actuatorButton.setFocusable(false);
actuatorButton.addActionListener((e) -> {
final boolean state = actuatorButton.isSelected();
UiUtils.submitUiMachineTask(() -> {
actuator.actuate(state);
});
});
panelActuators.add(actuatorButton);
actuatorButtons.put(actuator, actuatorButton);
}
private void removeActuator(Actuator actuator) {
panelActuators.remove(actuatorButtons.remove(actuator));
}
private ConfigurationListener configurationListener = new ConfigurationListener.Adapter() {
@Override
public void configurationComplete(Configuration configuration) throws Exception {
setUnits(configuration.getSystemUnits());
speedSlider.setValue((int) (configuration.getMachine().getSpeed() * 100));
panelActuators.removeAll();
Machine machine = Configuration.get().getMachine();
for (Actuator actuator : machine.getActuators()) {
addActuator(actuator);
}
for (final Head head : machine.getHeads()) {
for (Actuator actuator : head.getActuators()) {
addActuator(actuator);
}
for (final PasteDispenser dispenser : head.getPasteDispensers()) {
final JButton dispenserButton =
new JButton(head.getName() + ":" + dispenser.getName());
dispenserButton.setFocusable(false);
dispenserButton.addActionListener((e) -> {
UiUtils.submitUiMachineTask(() -> {
dispenser.dispense(null, null, 250);
});
});
panelDispensers.add(dispenserButton);
}
}
PropertyChangeListener listener = (e) -> {
if (e.getOldValue() == null && e.getNewValue() != null) {
Actuator actuator = (Actuator) e.getNewValue();
addActuator(actuator);
}
else if (e.getOldValue() != null && e.getNewValue() == null) {
removeActuator((Actuator) e.getOldValue());
}
};
BeanUtils.addPropertyChangeListener(machine, "actuators", listener);
for (Head head : machine.getHeads()) {
BeanUtils.addPropertyChangeListener(head, "actuators", listener);
}
setEnabled(machineControlsPanel.isEnabled());
}
};
private Map<Actuator, JToggleButton> actuatorButtons = new HashMap<>();
private JSlider speedSlider;
}