/*
* 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.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Locale;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import org.openpnp.ConfigurationListener;
import org.openpnp.gui.support.Icons;
import org.openpnp.gui.support.MessageBoxes;
import org.openpnp.gui.support.NozzleItem;
import org.openpnp.model.Configuration;
import org.openpnp.model.Location;
import org.openpnp.spi.Camera;
import org.openpnp.spi.Head;
import org.openpnp.spi.HeadMountable;
import org.openpnp.spi.Machine;
import org.openpnp.spi.MachineListener;
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;
public class MachineControlsPanel extends JPanel {
private final Configuration configuration;
private Nozzle selectedNozzle;
private JTextField textFieldX;
private JTextField textFieldY;
private JTextField textFieldC;
private JTextField textFieldZ;
private JComboBox comboBoxNozzles;
private Color droNormalColor = new Color(0xBDFFBE);
private Color droEditingColor = new Color(0xF0F0A1);
private Color droSavedColor = new Color(0x90cce0);
private JogControlsPanel jogControlsPanel;
private volatile double savedX = Double.NaN, savedY = Double.NaN, savedZ = Double.NaN,
savedC = Double.NaN;
/**
* Create the panel.
*/
public MachineControlsPanel(Configuration configuration) {
this.configuration = configuration;
jogControlsPanel = new JogControlsPanel(configuration, this);
createUi();
configuration.addListener(configurationListener);
}
public void setSelectedNozzle(Nozzle nozzle) {
selectedNozzle = nozzle;
comboBoxNozzles.setSelectedItem(selectedNozzle);
updateDros();
}
public Nozzle getSelectedNozzle() {
return selectedNozzle;
}
public PasteDispenser getSelectedPasteDispenser() {
try {
// TODO: We don't actually have a way to select a dispenser yet, so
// until we do we just return the first one.
return Configuration.get().getMachine().getDefaultHead().getDefaultPasteDispenser();
}
catch (Exception e) {
return null;
}
}
/**
* Currently returns the selected Nozzle. Intended to eventually return either the selected
* Nozzle or PasteDispenser.
* @return
*/
public HeadMountable getSelectedTool() {
return getSelectedNozzle();
}
public JogControlsPanel getJogControlsPanel() {
return jogControlsPanel;
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
homeAction.setEnabled(enabled);
jogControlsPanel.setEnabled(enabled);
targetCameraAction.setEnabled(enabled);
targetToolAction.setEnabled(enabled);
}
public Location getCurrentLocation() {
if (selectedNozzle == null) {
return null;
}
Location l = selectedNozzle.getLocation();
l = l.convertToUnits(configuration.getSystemUnits());
return l;
}
public void updateDros() {
Location l = getCurrentLocation();
if (l == null) {
return;
}
double x, y, z, c;
x = l.getX();
y = l.getY();
z = l.getZ();
c = l.getRotation();
double savedX = this.savedX;
if (!Double.isNaN(savedX)) {
x -= savedX;
}
double savedY = this.savedY;
if (!Double.isNaN(savedY)) {
y -= savedY;
}
double savedZ = this.savedZ;
if (!Double.isNaN(savedZ)) {
z -= savedZ;
}
double savedC = this.savedC;
if (!Double.isNaN(savedC)) {
c -= savedC;
}
textFieldX.setText(String.format(Locale.US, configuration.getLengthDisplayFormat(), x));
textFieldY.setText(String.format(Locale.US, configuration.getLengthDisplayFormat(), y));
textFieldZ.setText(String.format(Locale.US, configuration.getLengthDisplayFormat(), z));
textFieldC.setText(String.format(Locale.US, configuration.getLengthDisplayFormat(), c));
}
private void createUi() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
ButtonGroup buttonGroup = new ButtonGroup();
JPanel panel = new JPanel();
add(panel);
panel.setLayout(new FormLayout(
new ColumnSpec[] {FormSpecs.RELATED_GAP_COLSPEC,
ColumnSpec.decode("default:grow"),},
new RowSpec[] {FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,}));
comboBoxNozzles = new JComboBox();
comboBoxNozzles.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setSelectedNozzle(((NozzleItem) comboBoxNozzles.getSelectedItem()).getNozzle());
}
});
panel.add(comboBoxNozzles, "2, 2, fill, default");
JPanel panelDrosParent = new JPanel();
add(panelDrosParent);
panelDrosParent.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
JPanel panelDros = new JPanel();
panelDrosParent.add(panelDros);
panelDros.setLayout(new BoxLayout(panelDros, BoxLayout.Y_AXIS));
JPanel panelDrosFirstLine = new JPanel();
panelDros.add(panelDrosFirstLine);
panelDrosFirstLine.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
JLabel lblX = new JLabel("X");
lblX.setFont(new Font("Lucida Grande", Font.BOLD, 24));
panelDrosFirstLine.add(lblX);
textFieldX = new JTextField();
textFieldX.setEditable(false);
textFieldX.setFocusTraversalKeysEnabled(false);
textFieldX.setSelectionColor(droEditingColor);
textFieldX.setDisabledTextColor(Color.BLACK);
textFieldX.setBackground(droNormalColor);
textFieldX.setFont(new Font("Lucida Grande", Font.BOLD, 24));
textFieldX.setText("0000.0000");
textFieldX.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
savedX = Double.NaN;
}
saveXAction.actionPerformed(null);
}
});
panelDrosFirstLine.add(textFieldX);
textFieldX.setColumns(6);
Component horizontalStrut = Box.createHorizontalStrut(15);
panelDrosFirstLine.add(horizontalStrut);
JLabel lblY = new JLabel("Y");
lblY.setFont(new Font("Lucida Grande", Font.BOLD, 24));
panelDrosFirstLine.add(lblY);
textFieldY = new JTextField();
textFieldY.setEditable(false);
textFieldY.setFocusTraversalKeysEnabled(false);
textFieldY.setSelectionColor(droEditingColor);
textFieldY.setDisabledTextColor(Color.BLACK);
textFieldY.setBackground(droNormalColor);
textFieldY.setFont(new Font("Lucida Grande", Font.BOLD, 24));
textFieldY.setText("0000.0000");
textFieldY.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
savedY = Double.NaN;
}
saveYAction.actionPerformed(null);
}
});
panelDrosFirstLine.add(textFieldY);
textFieldY.setColumns(6);
JButton btnTargetTool = new JButton(targetToolAction);
panelDrosFirstLine.add(btnTargetTool);
btnTargetTool.setToolTipText("Position the tool at the camera's current location.");
JPanel panelDrosSecondLine = new JPanel();
panelDros.add(panelDrosSecondLine);
panelDrosSecondLine.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
JLabel lblC = new JLabel("C");
lblC.setFont(new Font("Lucida Grande", Font.BOLD, 24));
panelDrosSecondLine.add(lblC);
textFieldC = new JTextField();
textFieldC.setEditable(false);
textFieldC.setFocusTraversalKeysEnabled(false);
textFieldC.setSelectionColor(droEditingColor);
textFieldC.setDisabledTextColor(Color.BLACK);
textFieldC.setBackground(droNormalColor);
textFieldC.setText("0000.0000");
textFieldC.setFont(new Font("Lucida Grande", Font.BOLD, 24));
textFieldC.setColumns(6);
textFieldC.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
savedC = Double.NaN;
}
saveCAction.actionPerformed(null);
}
});
panelDrosSecondLine.add(textFieldC);
Component horizontalStrut_1 = Box.createHorizontalStrut(15);
panelDrosSecondLine.add(horizontalStrut_1);
JLabel lblZ = new JLabel("Z");
lblZ.setFont(new Font("Lucida Grande", Font.BOLD, 24));
panelDrosSecondLine.add(lblZ);
textFieldZ = new JTextField();
textFieldZ.setEditable(false);
textFieldZ.setFocusTraversalKeysEnabled(false);
textFieldZ.setSelectionColor(droEditingColor);
textFieldZ.setDisabledTextColor(Color.BLACK);
textFieldZ.setBackground(droNormalColor);
textFieldZ.setText("0000.0000");
textFieldZ.setFont(new Font("Lucida Grande", Font.BOLD, 24));
textFieldZ.setColumns(6);
textFieldZ.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
savedZ = Double.NaN;
}
saveZAction.actionPerformed(null);
}
});
panelDrosSecondLine.add(textFieldZ);
JButton btnTargetCamera = new JButton(targetCameraAction);
panelDrosSecondLine.add(btnTargetCamera);
btnTargetCamera.setToolTipText("Position the camera at the tool's current location.");
add(jogControlsPanel);
}
public Action startStopMachineAction = new AbstractAction("Stop", Icons.powerOn) {
@Override
public void actionPerformed(ActionEvent arg0) {
setEnabled(false);
new Thread(() -> {
Machine machine = Configuration.get().getMachine();
boolean enable = !machine.isEnabled();
try {
Configuration.get().getMachine().setEnabled(enable);
setEnabled(true);
}
catch (Exception t) {
MessageBoxes.errorBox(MachineControlsPanel.this, "Enable Failure",
t.getMessage());
setEnabled(true);
}
}).start();
}
};
@SuppressWarnings("serial")
public Action homeAction = new AbstractAction("Home", Icons.home) {
@Override
public void actionPerformed(ActionEvent arg0) {
UiUtils.submitUiMachineTask(() -> {
selectedNozzle.getHead().home();
});
}
};
@SuppressWarnings("serial")
public Action targetToolAction = new AbstractAction(null, Icons.centerTool) {
@Override
public void actionPerformed(ActionEvent arg0) {
UiUtils.submitUiMachineTask(() -> {
HeadMountable tool = getSelectedTool();
Camera camera = tool.getHead().getDefaultCamera();
MovableUtils.moveToLocationAtSafeZ(tool, camera.getLocation());
});
}
};
@SuppressWarnings("serial")
public Action targetCameraAction = new AbstractAction(null, Icons.centerCamera) {
@Override
public void actionPerformed(ActionEvent arg0) {
UiUtils.submitUiMachineTask(() -> {
HeadMountable tool = getSelectedTool();
Camera camera = tool.getHead().getDefaultCamera();
MovableUtils.moveToLocationAtSafeZ(camera, tool.getLocation());
});
}
};
@SuppressWarnings("serial")
public Action saveXAction = new AbstractAction(null) {
@Override
public void actionPerformed(ActionEvent arg0) {
if (Double.isNaN(savedX)) {
textFieldX.setBackground(droSavedColor);
savedX = getCurrentLocation().getX();
}
else {
textFieldX.setBackground(droNormalColor);
savedX = Double.NaN;
}
EventQueue.invokeLater(new Runnable() {
public void run() {
updateDros();
}
});
}
};
@SuppressWarnings("serial")
public Action saveYAction = new AbstractAction(null) {
@Override
public void actionPerformed(ActionEvent arg0) {
if (Double.isNaN(savedY)) {
textFieldY.setBackground(droSavedColor);
savedY = getCurrentLocation().getY();
}
else {
textFieldY.setBackground(droNormalColor);
savedY = Double.NaN;
}
EventQueue.invokeLater(new Runnable() {
public void run() {
updateDros();
}
});
}
};
@SuppressWarnings("serial")
public Action saveZAction = new AbstractAction(null) {
@Override
public void actionPerformed(ActionEvent arg0) {
if (Double.isNaN(savedZ)) {
textFieldZ.setBackground(droSavedColor);
savedZ = getCurrentLocation().getZ();
}
else {
textFieldZ.setBackground(droNormalColor);
savedZ = Double.NaN;
}
EventQueue.invokeLater(new Runnable() {
public void run() {
updateDros();
}
});
}
};
@SuppressWarnings("serial")
public Action saveCAction = new AbstractAction(null) {
@Override
public void actionPerformed(ActionEvent arg0) {
if (Double.isNaN(savedC)) {
textFieldC.setBackground(droSavedColor);
savedC = getCurrentLocation().getRotation();
}
else {
textFieldC.setBackground(droNormalColor);
savedC = Double.NaN;
}
EventQueue.invokeLater(new Runnable() {
public void run() {
updateDros();
}
});
}
};
private void updateStartStopButton(boolean enabled) {
startStopMachineAction.putValue(Action.NAME, enabled ? "Stop" : "Start");
startStopMachineAction.putValue(Action.SMALL_ICON,
enabled ? Icons.powerOff : Icons.powerOn);
}
private MachineListener machineListener = new MachineListener.Adapter() {
@Override
public void machineHeadActivity(Machine machine, Head head) {
EventQueue.invokeLater(() -> updateDros());
}
@Override
public void machineEnabled(Machine machine) {
updateStartStopButton(machine.isEnabled());
setEnabled(true);
EventQueue.invokeLater(() -> updateDros());
}
@Override
public void machineEnableFailed(Machine machine, String reason) {
updateStartStopButton(machine.isEnabled());
}
@Override
public void machineDisabled(Machine machine, String reason) {
updateStartStopButton(machine.isEnabled());
setEnabled(false);
}
@Override
public void machineDisableFailed(Machine machine, String reason) {
updateStartStopButton(machine.isEnabled());
}
};
private ConfigurationListener configurationListener = new ConfigurationListener.Adapter() {
@Override
public void configurationComplete(Configuration configuration) {
Machine machine = configuration.getMachine();
if (machine != null) {
machine.removeListener(machineListener);
}
for (Head head : machine.getHeads()) {
for (Nozzle nozzle : head.getNozzles()) {
comboBoxNozzles.addItem(new NozzleItem(nozzle));
}
}
setSelectedNozzle(((NozzleItem) comboBoxNozzles.getItemAt(0)).getNozzle());
machine.addListener(machineListener);
updateStartStopButton(machine.isEnabled());
setEnabled(machine.isEnabled());
for (Head head : machine.getHeads()) {
BeanUtils.addPropertyChangeListener(head, "nozzles", (e) -> {
if (e.getOldValue() == null && e.getNewValue() != null) {
Nozzle nozzle = (Nozzle) e.getNewValue();
comboBoxNozzles.addItem(new NozzleItem(nozzle));
}
else if (e.getOldValue() != null && e.getNewValue() == null) {
for (int i = 0; i < comboBoxNozzles.getItemCount(); i++) {
NozzleItem item = (NozzleItem) comboBoxNozzles.getItemAt(i);
if (item.getNozzle() == e.getOldValue()) {
comboBoxNozzles.removeItemAt(i);
}
}
}
});
}
}
};
}