diff --git a/.gitignore b/.gitignore index ac09a347177..18d33927e04 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ installers .classpath src/main/resources/icons/src/Soft_Scraps_by_deleket .github_changelog_generator +hs_err* +*.log diff --git a/src/main/java/org/openpnp/gui/JobPanel.java b/src/main/java/org/openpnp/gui/JobPanel.java index fa40d12a36f..347ae442042 100644 --- a/src/main/java/org/openpnp/gui/JobPanel.java +++ b/src/main/java/org/openpnp/gui/JobPanel.java @@ -95,6 +95,7 @@ import org.openpnp.util.FiniteStateMachine; import org.openpnp.util.MovableUtils; import org.openpnp.util.UiUtils; +import org.pmw.tinylog.Logger; import com.google.common.eventbus.Subscribe; @@ -245,7 +246,12 @@ else if (e.getFirstRow() == 0 && e.getLastRow() == Integer.MAX_VALUE) { updatePanelizationIconState(); } - + if (job.getBoardLocations().size() > 0) { + BoardLocation boardLocation = getSelectedBoardLocation(); + jobPlacementsPanel.setBoardLocation(boardLocation); + } + else + jobPlacementsPanel.setBoardLocation(null); } }); diff --git a/src/main/java/org/openpnp/gui/JobPlacementsPanel.java b/src/main/java/org/openpnp/gui/JobPlacementsPanel.java index 1303d49f886..ace28d51eba 100644 --- a/src/main/java/org/openpnp/gui/JobPlacementsPanel.java +++ b/src/main/java/org/openpnp/gui/JobPlacementsPanel.java @@ -12,6 +12,7 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; +import java.util.regex.PatternSyntaxException; import javax.swing.AbstractAction; import javax.swing.Action; @@ -26,11 +27,13 @@ import javax.swing.JTable; import javax.swing.JToolBar; import javax.swing.ListSelectionModel; +import javax.swing.RowFilter; import javax.swing.UIManager; import javax.swing.border.LineBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableRowSorter; import org.openpnp.events.PlacementSelectedEvent; import org.openpnp.gui.components.AutoSelectTextTable; @@ -60,10 +63,12 @@ import org.openpnp.util.MovableUtils; import org.openpnp.util.UiUtils; import org.openpnp.util.Utils2D; +import org.pmw.tinylog.Logger; public class JobPlacementsPanel extends JPanel { private JTable table; private PlacementsTableModel tableModel; + private TableRowSorter tableSorter; private ActionGroup boardLocationSelectionActionGroup; private ActionGroup singleSelectionActionGroup; private ActionGroup multiSelectionActionGroup; @@ -143,9 +148,10 @@ public JobPlacementsPanel(JobPanel jobPanel) { toolBarPlacements.add(btnEditFeeder); tableModel = new PlacementsTableModel(configuration); + tableSorter = new TableRowSorter<>(tableModel); table = new AutoSelectTextTable(tableModel); - table.setAutoCreateRowSorter(true); + table.setRowSorter(tableSorter); table.getTableHeader().setDefaultRenderer(new MultisortTableHeaderCellRenderer()); table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); table.setDefaultEditor(Side.class, new DefaultCellEditor(sidesComboBox)); @@ -258,6 +264,17 @@ public void setBoardLocation(BoardLocation boardLocation) { else { tableModel.setBoardLocation(boardLocation); boardLocationSelectionActionGroup.setEnabled(true); + + RowFilter rf = null; + // If current expression doesn't parse, don't update. + try { + rf = RowFilter.regexFilter("(?i)" + boardLocation.getSide().toString()); + } + catch (PatternSyntaxException e) { + Logger.warn("Side sort failed", e); + return; + } + tableSorter.setRowFilter(rf); } } diff --git a/src/main/java/org/openpnp/gui/MachineControlsPanel.java b/src/main/java/org/openpnp/gui/MachineControlsPanel.java index 9725288900f..df40930abef 100644 --- a/src/main/java/org/openpnp/gui/MachineControlsPanel.java +++ b/src/main/java/org/openpnp/gui/MachineControlsPanel.java @@ -268,8 +268,11 @@ public void actionPerformed(ActionEvent arg0) { Machine machine = Configuration.get().getMachine(); boolean enable = !machine.isEnabled(); try { - Configuration.get().getMachine().setEnabled(enable); - setEnabled(true); + Configuration.get().getMachine().setEnabled(enable); + setEnabled(true); + if (machine.getHomeAfterEnabled() && machine.isEnabled()) { + selectedTool.getHead().home(); + } } catch (Exception t1) { MessageBoxes.errorBox(MachineControlsPanel.this, "Enable Failure", diff --git a/src/main/java/org/openpnp/gui/MainFrame.java b/src/main/java/org/openpnp/gui/MainFrame.java index 86d5d6bc552..fe3434075b4 100644 --- a/src/main/java/org/openpnp/gui/MainFrame.java +++ b/src/main/java/org/openpnp/gui/MainFrame.java @@ -107,6 +107,24 @@ public class MainFrame extends JFrame { private static final String PREF_WINDOW_STYLE_MULTIPLE = "MainFrame.windowStyleMultiple"; private static final boolean PREF_WINDOW_STYLE_MULTIPLE_DEF = false; + private static final String PREF_CAMERA_WINDOW_X = "CameraFrame.windowX"; + private static final int PREF_CAMERA_WINDOW_X_DEF = 0; + private static final String PREF_CAMERA_WINDOW_Y = "CameraFrame.windowY"; + private static final int PREF_CAMERA_WINDOW_Y_DEF = 0; + private static final String PREF_CAMERA_WINDOW_WIDTH = "CameraFrame.windowWidth"; + private static final int PREF_CAMERA_WINDOW_WIDTH_DEF = 800; + private static final String PREF_CAMERA_WINDOW_HEIGHT = "CameraFrame.windowHeight"; + private static final int PREF_CAMERA_WINDOW_HEIGHT_DEF = 600; + + private static final String PREF_MACHINECONTROLS_WINDOW_X = "MachineControlsFrame.windowX"; + private static final int PREF_MACHINECONTROLS_WINDOW_X_DEF = 0; + private static final String PREF_MACHINECONTROLS_WINDOW_Y = "MachineControlsFrame.windowY"; + private static final int PREF_MACHINECONTROLS_WINDOW_Y_DEF = 0; + private static final String PREF_MACHINECONTROLS_WINDOW_WIDTH = "MachineControlsFrame.windowWidth"; + private static final int PREF_MACHINECONTROLS_WINDOW_WIDTH_DEF = 490; + private static final String PREF_MACHINECONTROLS_WINDOW_HEIGHT = "MachineControlsFrame.windowHeight"; + private static final int PREF_MACHINECONTROLS_WINDOW_HEIGHT_DEF = 340; + private final Configuration configuration; private static MainFrame mainFrame; @@ -120,6 +138,8 @@ public class MainFrame extends JFrame { private JPanel panelCameraAndInstructions; private JPanel panelMachine; private MachineSetupPanel machineSetupPanel; + private JDialog frameCamera; + private JDialog frameMachineControls; public static MainFrame get() { return mainFrame; @@ -232,6 +252,8 @@ public void windowClosing(WindowEvent e) { mnFile.addSeparator(); mnFile.add(new JMenuItem(jobPanel.saveJobAction)); mnFile.add(new JMenuItem(jobPanel.saveJobAsAction)); + mnFile.addSeparator(); + mnFile.add(new JMenuItem(saveConfigAction)); // File -> Import @@ -564,21 +586,47 @@ public void propertyChange(PropertyChangeEvent evt) { public void splitWindows() { if (prefs.getBoolean(PREF_WINDOW_STYLE_MULTIPLE, PREF_WINDOW_STYLE_MULTIPLE_DEF)) { // pin panelCameraAndInstructions to a separate JFrame - JDialog frameCamera = new JDialog(this, "OpenPnp - Camera", false); + frameCamera = new JDialog(this, "OpenPnp - Camera", false); // as of today no smart way found to get an adjusted size // ... so main window size is used for the camera window - frameCamera.setSize(getFrames()[0].getSize()); frameCamera.add(panelCameraAndInstructions); frameCamera.setVisible(true); + frameCamera.addComponentListener(cameraWindowListener); + + if (prefs.getInt(PREF_CAMERA_WINDOW_WIDTH, 50) < 50) { + prefs.putInt(PREF_CAMERA_WINDOW_WIDTH, PREF_CAMERA_WINDOW_WIDTH_DEF); + } + + if (prefs.getInt(PREF_CAMERA_WINDOW_HEIGHT, 50) < 50) { + prefs.putInt(PREF_CAMERA_WINDOW_HEIGHT, PREF_CAMERA_WINDOW_HEIGHT_DEF); + } + + frameCamera.setBounds(prefs.getInt(PREF_CAMERA_WINDOW_X, PREF_CAMERA_WINDOW_X_DEF), + prefs.getInt(PREF_CAMERA_WINDOW_Y, PREF_CAMERA_WINDOW_Y_DEF), + prefs.getInt(PREF_CAMERA_WINDOW_WIDTH, PREF_CAMERA_WINDOW_WIDTH_DEF), + prefs.getInt(PREF_CAMERA_WINDOW_HEIGHT, PREF_CAMERA_WINDOW_HEIGHT_DEF)); // pin machineControlsPanel to a separate JFrame - JDialog frameMachineControls = new JDialog(this, "OpenPnp - Machine Controls", false); + frameMachineControls = new JDialog(this, "OpenPnp - Machine Controls", false); // as of today no smart way found to get an adjusted size // ... so hardcoded values used (usually not a good idea) frameMachineControls.add(machineControlsPanel); frameMachineControls.setVisible(true); frameMachineControls.pack(); + frameMachineControls.addComponentListener(machineControlsWindowListener); + + if (prefs.getInt(PREF_MACHINECONTROLS_WINDOW_WIDTH, 50) < 50) { + prefs.putInt(PREF_MACHINECONTROLS_WINDOW_WIDTH, PREF_MACHINECONTROLS_WINDOW_WIDTH_DEF); + } + + if (prefs.getInt(PREF_MACHINECONTROLS_WINDOW_HEIGHT, 50) < 50) { + prefs.putInt(PREF_MACHINECONTROLS_WINDOW_HEIGHT, PREF_MACHINECONTROLS_WINDOW_HEIGHT_DEF); + } + frameMachineControls.setBounds(prefs.getInt(PREF_MACHINECONTROLS_WINDOW_X, PREF_MACHINECONTROLS_WINDOW_X_DEF), + prefs.getInt(PREF_MACHINECONTROLS_WINDOW_Y, PREF_MACHINECONTROLS_WINDOW_Y_DEF), + prefs.getInt(PREF_MACHINECONTROLS_WINDOW_WIDTH, PREF_MACHINECONTROLS_WINDOW_WIDTH_DEF), + prefs.getInt(PREF_MACHINECONTROLS_WINDOW_HEIGHT, PREF_MACHINECONTROLS_WINDOW_HEIGHT_DEF)); // move the splitPaneDivider to position 0 to fill the gap of the // relocated panels 'panelCameraAndInstructions' & 'machineControlsPanel' splitPaneMachineAndTabs.setDividerLocation(0); @@ -700,6 +748,25 @@ public void about() { dialog.setVisible(true); } + public boolean saveConfig() { + // Save the configuration + try { + configuration.save(); + } + catch (Exception e) { + String message = "There was a problem saving the configuration. The reason was:\n\n" + e.getMessage() + + "\n\n"; + message = message.replaceAll("\n", "
"); + message = message.replaceAll("\r", ""); + message = "" + message + ""; + JOptionPane.showMessageDialog(this, message, "Configuration Save Error", JOptionPane.ERROR_MESSAGE); + return false; + } + + Logger.debug("Config saved successfully!"); + return true; + } + public boolean quit() { Logger.info("Shutting down..."); try { @@ -772,6 +839,34 @@ public void componentResized(ComponentEvent e) { } }; + private ComponentListener cameraWindowListener = new ComponentAdapter() { + @Override + public void componentMoved(ComponentEvent e) { + prefs.putInt(PREF_CAMERA_WINDOW_X, frameCamera.getLocation().x); + prefs.putInt(PREF_CAMERA_WINDOW_Y, frameCamera.getLocation().y); + } + + @Override + public void componentResized(ComponentEvent e) { + prefs.putInt(PREF_CAMERA_WINDOW_WIDTH, frameCamera.getSize().width); + prefs.putInt(PREF_CAMERA_WINDOW_HEIGHT, frameCamera.getSize().height); + } + }; + + private ComponentListener machineControlsWindowListener = new ComponentAdapter() { + @Override + public void componentMoved(ComponentEvent e) { + prefs.putInt(PREF_MACHINECONTROLS_WINDOW_X, frameMachineControls.getLocation().x); + prefs.putInt(PREF_MACHINECONTROLS_WINDOW_Y, frameMachineControls.getLocation().y); + } + + @Override + public void componentResized(ComponentEvent e) { + prefs.putInt(PREF_MACHINECONTROLS_WINDOW_WIDTH, frameMachineControls.getSize().width); + prefs.putInt(PREF_MACHINECONTROLS_WINDOW_HEIGHT, frameMachineControls.getSize().height); + } + }; + private Action inchesUnitSelected = new AbstractAction(LengthUnit.Inches.name()) { { putValue(MNEMONIC_KEY, KeyEvent.VK_I); @@ -816,6 +911,13 @@ public void actionPerformed(ActionEvent arg0) { } }; + private Action saveConfigAction = new AbstractAction("Save configuration") { + @Override + public void actionPerformed(ActionEvent arg0) { + saveConfig(); + } + }; + private Action quitAction = new AbstractAction("Exit") { { putValue(MNEMONIC_KEY, KeyEvent.VK_X); diff --git a/src/main/java/org/openpnp/gui/components/CameraPanel.java b/src/main/java/org/openpnp/gui/components/CameraPanel.java index 692dc211662..394f3474e18 100644 --- a/src/main/java/org/openpnp/gui/components/CameraPanel.java +++ b/src/main/java/org/openpnp/gui/components/CameraPanel.java @@ -45,7 +45,8 @@ public class CameraPanel extends JPanel { private static int maximumFps = 15; private static final String SHOW_NONE_ITEM = "Show None"; - private static final String SHOW_ALL_ITEM = "Show All"; + private static final String SHOW_ALL_ITEM_H = "Show All Horizontal"; + private static final String SHOW_ALL_ITEM_V = "Show All Vertical"; private Map cameraViews = new LinkedHashMap<>(); @@ -111,7 +112,8 @@ public void addCamera(Camera camera) { else if (cameraViews.size() == 2) { // Otherwise this is the second camera so mix in the // show all item. - camerasCombo.insertItemAt(SHOW_ALL_ITEM, 1); + camerasCombo.insertItemAt(SHOW_ALL_ITEM_H, 1); + camerasCombo.insertItemAt(SHOW_ALL_ITEM_V, 2); } } @@ -154,7 +156,7 @@ private void createUi() { * @return */ public void ensureCameraVisible(Camera camera) { - if (camerasCombo.getSelectedItem().equals(SHOW_ALL_ITEM)) { + if (camerasCombo.getSelectedItem().equals(SHOW_ALL_ITEM_H) || camerasCombo.getSelectedItem().equals(SHOW_ALL_ITEM_V)) { return; } setSelectedCamera(camera); @@ -193,12 +195,16 @@ public void actionPerformed(ActionEvent ev) { camerasPanel.add(panel); selectedCameraView = null; } - else if (camerasCombo.getSelectedItem().equals(SHOW_ALL_ITEM)) { + else if (camerasCombo.getSelectedItem().equals(SHOW_ALL_ITEM_H) || camerasCombo.getSelectedItem().equals(SHOW_ALL_ITEM_V)) { int rows = (int) Math.ceil(Math.sqrt(cameraViews.size())); if (rows == 0) { rows = 1; } - camerasPanel.setLayout(new GridLayout(rows, 0, 1, 1)); + if (camerasCombo.getSelectedItem().equals(SHOW_ALL_ITEM_H)) + camerasPanel.setLayout(new GridLayout(rows, 0, 1, 1)); + else + camerasPanel.setLayout(new GridLayout(0, rows, 1, 1)); + for (CameraView cameraView : cameraViews.values()) { cameraView.setMaximumFps(maximumFps / Math.max(cameraViews.size(), 1)); cameraView.setShowName(true); diff --git a/src/main/java/org/openpnp/machine/reference/ReferenceMachine.java b/src/main/java/org/openpnp/machine/reference/ReferenceMachine.java index 385f5e488af..314fc0512dc 100644 --- a/src/main/java/org/openpnp/machine/reference/ReferenceMachine.java +++ b/src/main/java/org/openpnp/machine/reference/ReferenceMachine.java @@ -86,6 +86,12 @@ public class ReferenceMachine extends AbstractMachine { @Element(required = false) protected FiducialLocator fiducialLocator = new ReferenceFiducialLocator(); + @Element(required = false) + private boolean homeAfterEnabled = false; + + @Element(required = false) + private boolean usePickRotationInsteadOfRotationInTapeForStripFeeders = false; + private boolean enabled; private List> registeredFeederClasses = new ArrayList<>(); @@ -298,4 +304,20 @@ public PnpJobProcessor getPnpJobProcessor() { public PasteDispenseJobProcessor getPasteDispenseJobProcessor() { return pasteDispenseJobProcessor; } + + public boolean getHomeAfterEnabled() { + return homeAfterEnabled; + } + + public boolean getUsePickRotationInsteadOfRotationInTapeForStripFeeders() { + return usePickRotationInsteadOfRotationInTapeForStripFeeders; + } + + public void setHomeAfterEnabled(boolean newValue) { + this.homeAfterEnabled = newValue; + } + + public void setUsePickRotationInsteadOfRotationInTapeForStripFeeders(boolean newValue) { + this.usePickRotationInsteadOfRotationInTapeForStripFeeders = newValue; + } } diff --git a/src/main/java/org/openpnp/machine/reference/ReferencePnpJobProcessor.java b/src/main/java/org/openpnp/machine/reference/ReferencePnpJobProcessor.java index 5aa655deb57..509df088169 100644 --- a/src/main/java/org/openpnp/machine/reference/ReferencePnpJobProcessor.java +++ b/src/main/java/org/openpnp/machine/reference/ReferencePnpJobProcessor.java @@ -19,6 +19,7 @@ package org.openpnp.machine.reference; +import java.io.File; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; @@ -426,8 +427,9 @@ protected void doPlan() throws Exception { fireTextStatus("Planning placements."); // Get the list of unfinished placements and sort them by part height. + List jobPlacements = getPendingJobPlacements().stream() - .sorted(Comparator.comparing(JobPlacement::getPartHeight)) + .sorted(Comparator.comparing(JobPlacement::getPartId)) .collect(Collectors.toList()); if (jobPlacements.isEmpty()) { @@ -788,6 +790,10 @@ protected void doPlace() throws Exception { } Logger.debug("Place {} with {}", part, nozzle.getName()); + + File file = job.getFile(); + Configuration.get().saveJob(job, file); + Configuration.get().save(); } clearStepComplete(); diff --git a/src/main/java/org/openpnp/machine/reference/feeder/ReferenceDragFeeder.java b/src/main/java/org/openpnp/machine/reference/feeder/ReferenceDragFeeder.java index 1c087cd5188..d5a51e78ce3 100644 --- a/src/main/java/org/openpnp/machine/reference/feeder/ReferenceDragFeeder.java +++ b/src/main/java/org/openpnp/machine/reference/feeder/ReferenceDragFeeder.java @@ -79,6 +79,8 @@ public class ReferenceDragFeeder extends ReferenceFeeder { protected double feedSpeed = 1.0; @Attribute(required = false) protected String actuatorName; + @Attribute(required = false) + protected String peelOffActuatorName; @Element(required = false) protected Vision vision = new Vision(); @Element(required = false) @@ -86,6 +88,10 @@ public class ReferenceDragFeeder extends ReferenceFeeder { protected Location pickLocation; + private double feededCount = 0; + private double partsPitchX = -2; //-2mm for 0402 + private double partsPitchY = 0; + /* * visionOffset contains the difference between where the part was expected to be and where it * is. Subtracting these offsets from the pickLocation produces the correct pick location. @@ -93,12 +99,21 @@ public class ReferenceDragFeeder extends ReferenceFeeder { * correct feed locations. */ protected Location visionOffset; + protected Location partPitch; @Override public Location getPickLocation() throws Exception { if (pickLocation == null) { pickLocation = location; } + + if (vision.isEnabled() && visionOffset != null) { + if (this.isPart_0402() && partPitch != null) + return pickLocation.subtract(visionOffset).add(partPitch); + else + return pickLocation.subtract(visionOffset); + } + return pickLocation; } @@ -127,6 +142,17 @@ public void feed(Nozzle nozzle) throws Exception { actuatorName, head.getName())); } + Actuator peelOffActuator = null; + + if (peelOffActuatorName != null) { + peelOffActuator = head.getActuatorByName(peelOffActuatorName); + + if (peelOffActuator == null) { + throw new Exception(String.format("No Actuator found with name %s on feed Head %s", + peelOffActuatorName, head.getName())); + } + } + head.moveToSafeZ(); if (vision.isEnabled()) { @@ -140,6 +166,7 @@ public void feed(Nozzle nozzle) throws Exception { Logger.debug("First feed, running vision pre-flight."); visionOffset = getVisionOffsets(head, location); + feededCount = 0; } Logger.debug("visionOffsets " + visionOffset); } @@ -149,45 +176,69 @@ public void feed(Nozzle nozzle) throws Exception { // feedEndLocation and pickLocation. pickLocation will be saved // for the pick operation while feed start and end are used // here and then discarded. - Location feedStartLocation = this.feedStartLocation; - Location feedEndLocation = this.feedEndLocation; pickLocation = this.location; - if (visionOffset != null) { - feedStartLocation = feedStartLocation.subtract(visionOffset); - feedEndLocation = feedEndLocation.subtract(visionOffset); - pickLocation = pickLocation.subtract(visionOffset); - } - // Move the actuator to the feed start location. - actuator.moveTo(feedStartLocation.derive(null, null, Double.NaN, Double.NaN)); + if (feededCount == 0) { + Location feedStartLocation = this.feedStartLocation; + Location feedEndLocation = this.feedEndLocation; + if (vision.isEnabled() && visionOffset != null) { + feedStartLocation = feedStartLocation.subtract(visionOffset); + Logger.debug("New drag distance with visionOffset " + feedStartLocation.subtract(feedEndLocation)); + } - // extend the pin - actuator.actuate(true); + // Move the actuator to the feed start location. + actuator.moveTo(feedStartLocation.derive(null, null, Double.NaN, Double.NaN)); - // insert the pin - actuator.moveTo(feedStartLocation); + // extend the pin + actuator.actuate(true); - // drag the tape - actuator.moveTo(feedEndLocation, feedSpeed * actuator.getHead().getMachine().getSpeed()); - - // backoff to release tension from the pin - if (backoffDistance.getValue() != 0) { - Location backoffLocation = Utils2D.getPointAlongLine(feedEndLocation, feedStartLocation, backoffDistance); - actuator.moveTo(backoffLocation, feedSpeed * actuator.getHead().getMachine().getSpeed()); - } - - head.moveToSafeZ(); + // insert the pin + actuator.moveTo(feedStartLocation); + + // drag the tape + actuator.moveTo(feedEndLocation, feedSpeed * actuator.getHead().getMachine().getSpeed()); + + if (peelOffActuator != null) { + // peel off before backoff + peelOffActuator.actuate(true); + peelOffActuator.actuate(false); + } + + // backoff to release tension from the pin + if (backoffDistance.getValue() != 0) { + Location backoffLocation = Utils2D.getPointAlongLine(feedEndLocation, feedStartLocation, backoffDistance); + actuator.moveTo(backoffLocation, feedSpeed * actuator.getHead().getMachine().getSpeed()); + } - // retract the pin - actuator.actuate(false); + // retract the pin + actuator.actuate(false); + + if (this.isPart_0402() == true) { + // can change it to "feededCount = parts_count_userSettings;" + feededCount = 2; + } + } else + Logger.debug("Multi parts drag feeder: skipping drag " + feededCount); + + + head.moveToSafeZ(); if (vision.isEnabled()) { visionOffset = getVisionOffsets(head, location); + if (feededCount > 0) { + feededCount--; + if (feededCount > 0) { + partPitch = new Location(LengthUnit.Millimeters, partsPitchX * feededCount, + partsPitchY * feededCount, 0, 0); + } else + partPitch = null; + } + Logger.debug("final visionOffsets " + visionOffset); - } - Logger.debug("Modified pickLocation {}", pickLocation); + Logger.debug("Modified pickLocation {}", pickLocation.subtract(visionOffset)); + } } // TODO: Throw an Exception if vision fails. @@ -284,6 +335,20 @@ public String toString() { return String.format("ReferenceTapeFeeder id %s", id); } + public void resetVisionOffsets() { + if (visionOffset != null) { + visionOffset = null; + Logger.debug("resetVisionOffsets " + visionOffset); + } + + partPitch = null; + } + + public boolean isPart_0402() { + return this.getPart().getPackage().getId().contains("C0402") + || this.getPart().getPackage().getId().contains("R0402"); + } + public Location getFeedStartLocation() { return feedStartLocation; } @@ -318,6 +383,16 @@ public void setActuatorName(String actuatorName) { propertyChangeSupport.firePropertyChange("actuatorName", oldValue, actuatorName); } + public String getPeelOffActuatorName() { + return peelOffActuatorName; + } + + public void setPeelOffActuatorName(String actuatorName) { + String oldValue = this.peelOffActuatorName; + this.peelOffActuatorName = actuatorName; + propertyChangeSupport.firePropertyChange("actuatorName", oldValue, actuatorName); + } + public Length getBackoffDistance() { return backoffDistance; } diff --git a/src/main/java/org/openpnp/machine/reference/feeder/ReferenceStripFeeder.java b/src/main/java/org/openpnp/machine/reference/feeder/ReferenceStripFeeder.java index 5e7e996668e..b9ac22e1f40 100644 --- a/src/main/java/org/openpnp/machine/reference/feeder/ReferenceStripFeeder.java +++ b/src/main/java/org/openpnp/machine/reference/feeder/ReferenceStripFeeder.java @@ -30,11 +30,13 @@ import org.openpnp.gui.support.Wizard; import org.openpnp.machine.reference.ReferenceFeeder; import org.openpnp.machine.reference.feeder.wizards.ReferenceStripFeederConfigurationWizard; +import org.openpnp.model.Configuration; import org.openpnp.model.Length; import org.openpnp.model.LengthUnit; import org.openpnp.model.Location; import org.openpnp.model.Point; import org.openpnp.spi.Camera; +import org.openpnp.spi.Machine; import org.openpnp.spi.Nozzle; import org.openpnp.spi.PropertySheetHolder; import org.openpnp.util.MovableUtils; @@ -177,20 +179,49 @@ public Location getPickLocation() throws Exception { Length x = getHoleToPartLateral().convertToUnits(l.getUnits()); Length y = referenceHoleToPartLinear.convertToUnits(l.getUnits()); Point p = new Point(x.getValue(), y.getValue()); - // Determine the angle that the tape is at - double angle = Utils2D.getAngleFromPoint(lineLocations[0], lineLocations[1]); - // Rotate the part offsets by the angle to move it into the right - // coordinate space - p = Utils2D.rotatePoint(p, angle); - // And add the offset to the location we calculated previously - l = l.add(new Location(l.getUnits(), p.x, p.y, 0, 0)); - // Add in the angle of the tape plus the angle of the part in the tape - // so that the part is picked at the right angle - l = l.derive(null, null, null, angle + getLocation().getRotation()); + + Machine machine = Configuration.get().getMachine(); + + if (machine.getUsePickRotationInsteadOfRotationInTapeForStripFeeders()) { + + double angleCorrection = 0; + double angleOfStrip = Utils2D.getAngleFromPoint(lineLocations[0], lineLocations[1]); + + if (angleOfStrip > 90) + angleCorrection = angleOfStrip % 90; + else + angleCorrection = angleOfStrip; + + if (angleCorrection > 45) + angleCorrection = 90 - angleCorrection; + else + angleCorrection *= -1; + + // Rotate the part + p = Utils2D.rotatePoint(p, angleOfStrip); + l = l.add(new Location(l.getUnits(), p.x, p.y, 0, 0)); + l = l.derive(null, null, null, getLocation().getRotation() - angleCorrection); + + } else { + + // Determine the angle that the tape is at + double angle = Utils2D.getAngleFromPoint(lineLocations[0], lineLocations[1]); + // Rotate the part offsets by the angle to move it into the right + // coordinate space + p = Utils2D.rotatePoint(p, angle); + // And add the offset to the location we calculated previously + l = l.add(new Location(l.getUnits(), p.x, p.y, 0, 0)); + // Add in the angle of the tape plus the angle of the part in the tape + // so that the part is picked at the right angle + l = l.derive(null, null, null, angle + getLocation().getRotation()); + + } + // and if vision was performed, add the offsets if (visionEnabled && visionOffsets != null) { l = l.add(visionOffsets); } + return l; } diff --git a/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceDragFeederConfigurationWizard.java b/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceDragFeederConfigurationWizard.java index 4fd9e672b49..c88e2d6803e 100644 --- a/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceDragFeederConfigurationWizard.java +++ b/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceDragFeederConfigurationWizard.java @@ -80,7 +80,10 @@ public class ReferenceDragFeederConfigurationWizard private JTextField textFieldFeedEndZ; private JTextField textFieldFeedRate; private JLabel lblActuatorId; + private JLabel lblPeelOffActuatorId; + private JLabel lbl0402PartDetected; private JTextField textFieldActuatorId; + private JTextField textFieldPeelOffActuatorId; private JPanel panelGeneral; private JPanel panelVision; private JPanel panelLocations; @@ -106,6 +109,7 @@ public class ReferenceDragFeederConfigurationWizard private JButton btnCancelChangeAoi; private JPanel panel; private JButton btnCancelChangeTemplateImage; + private JButton btnResetVisionOffsets; public ReferenceDragFeederConfigurationWizard(ReferenceDragFeeder feeder) { super(feeder); @@ -121,6 +125,8 @@ public ReferenceDragFeederConfigurationWizard(ReferenceDragFeeder feeder) { panelFields.add(panelGeneral); panelGeneral.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,}, new RowSpec[] {FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC, FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,})); @@ -139,6 +145,18 @@ public ReferenceDragFeederConfigurationWizard(ReferenceDragFeeder feeder) { panelGeneral.add(textFieldActuatorId, "4, 4"); textFieldActuatorId.setColumns(5); + lblPeelOffActuatorId = new JLabel("Peel Off Actuator Name"); + panelGeneral.add(lblPeelOffActuatorId, "6, 4, right, default"); + + textFieldPeelOffActuatorId = new JTextField(); + panelGeneral.add(textFieldPeelOffActuatorId, "8, 4"); + textFieldPeelOffActuatorId.setColumns(5); + + if (feeder.isPart_0402()) { + lbl0402PartDetected = new JLabel("0402 Part DETECTED"); + panelGeneral.add(lbl0402PartDetected, "6, 2"); + } + panelLocations = new JPanel(); panelFields.add(panelLocations); panelLocations.setBorder(new TitledBorder(null, "Locations", TitledBorder.LEADING, @@ -282,8 +300,13 @@ public ReferenceDragFeederConfigurationWizard(ReferenceDragFeeder feeder) { FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC, FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC, FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,}, - new RowSpec[] {FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC, - FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,})); + 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, + })); lblX_1 = new JLabel("X"); panelAoE.add(lblX_1, "2, 2"); @@ -324,6 +347,10 @@ public ReferenceDragFeederConfigurationWizard(ReferenceDragFeeder feeder) { cancelSelectTemplateImageAction.setEnabled(false); cancelSelectAoiAction.setEnabled(false); + btnResetVisionOffsets = new JButton("Reset offsets"); + btnResetVisionOffsets.setAction(resetVisionOffsets); + panelAoE.add(btnResetVisionOffsets, "12, 10"); + contentPanel.add(panelFields); } @@ -339,6 +366,7 @@ public void createBindings() { addWrappedBinding(feeder, "feedSpeed", textFieldFeedRate, "text", percentConverter); addWrappedBinding(feeder, "actuatorName", textFieldActuatorId, "text"); + addWrappedBinding(feeder, "peelOffActuatorName", textFieldPeelOffActuatorId, "text"); MutableLocationProxy feedStartLocation = new MutableLocationProxy(); bind(UpdateStrategy.READ_WRITE, feeder, "feedStartLocation", feedStartLocation, "location"); @@ -371,6 +399,7 @@ public void createBindings() { ComponentDecorators.decorateWithAutoSelectAndLengthConversion(textFieldFeedRate); ComponentDecorators.decorateWithAutoSelect(textFieldActuatorId); + ComponentDecorators.decorateWithAutoSelect(textFieldPeelOffActuatorId); ComponentDecorators.decorateWithAutoSelectAndLengthConversion(textFieldFeedStartX); ComponentDecorators.decorateWithAutoSelectAndLengthConversion(textFieldFeedStartY); ComponentDecorators.decorateWithAutoSelectAndLengthConversion(textFieldFeedStartZ); @@ -521,6 +550,16 @@ public void actionPerformed(ActionEvent arg0) { }); } }; + + @SuppressWarnings("serial") + private Action resetVisionOffsets = new AbstractAction("Reset vision offsets") { + @Override + public void actionPerformed(ActionEvent arg0) { + UiUtils.messageBoxOnException(() -> { + feeder.resetVisionOffsets(); + }); + } + }; private JLabel lblBackoffDistance; private JTextField backoffDistTf; } diff --git a/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceStripFeederConfigurationWizard.java b/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceStripFeederConfigurationWizard.java index c3607da6c2f..104f0143f35 100644 --- a/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceStripFeederConfigurationWizard.java +++ b/src/main/java/org/openpnp/machine/reference/feeder/wizards/ReferenceStripFeederConfigurationWizard.java @@ -160,7 +160,7 @@ public ReferenceStripFeederConfigurationWizard(ReferenceStripFeeder feeder) { comboBoxPart.setRenderer(new IdentifiableListCellRenderer()); panelPart.add(comboBoxPart, "4, 2, left, default"); - lblRotationInTape = new JLabel("Rotation In Tape"); + lblRotationInTape = new JLabel(Configuration.get().getMachine().getUsePickRotationInsteadOfRotationInTapeForStripFeeders() ? "Pick Rotation" : "Rotation In Tape"); panelPart.add(lblRotationInTape, "2, 4, left, default"); textFieldLocationRotation = new JTextField(); diff --git a/src/main/java/org/openpnp/machine/reference/vision/ReferenceBottomVision.java b/src/main/java/org/openpnp/machine/reference/vision/ReferenceBottomVision.java index 906b514fc5b..bbbee9f9d02 100644 --- a/src/main/java/org/openpnp/machine/reference/vision/ReferenceBottomVision.java +++ b/src/main/java/org/openpnp/machine/reference/vision/ReferenceBottomVision.java @@ -120,6 +120,7 @@ private static PartAlignmentOffset findOffsetsPreRotate(Part part, BoardLocation Location offsets = VisionUtils.getPixelCenterOffsets(camera, rect.center.x, rect.center.y) .derive(null, null, null, Double.NaN); + Logger.debug("Final offsets {}", offsets); displayResult(pipeline, part, offsets, camera); return new PartAlignment.PartAlignmentOffset(offsets, true); } diff --git a/src/main/java/org/openpnp/machine/reference/wizards/ReferenceMachineConfigurationWizard.java b/src/main/java/org/openpnp/machine/reference/wizards/ReferenceMachineConfigurationWizard.java index 2b7b3ce677f..89a43c0fa88 100644 --- a/src/main/java/org/openpnp/machine/reference/wizards/ReferenceMachineConfigurationWizard.java +++ b/src/main/java/org/openpnp/machine/reference/wizards/ReferenceMachineConfigurationWizard.java @@ -1,6 +1,7 @@ package org.openpnp.machine.reference.wizards; import javax.swing.JComboBox; +import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; @@ -22,6 +23,7 @@ import org.openpnp.machine.reference.driver.LinuxCNC; import org.openpnp.machine.reference.driver.NullDriver; import org.openpnp.model.Configuration; +import org.simpleframework.xml.Element; import com.jgoodies.forms.layout.ColumnSpec; import com.jgoodies.forms.layout.FormLayout; @@ -30,8 +32,10 @@ public class ReferenceMachineConfigurationWizard extends AbstractConfigurationWizard { - final private ReferenceMachine machine; + private final ReferenceMachine machine; private JComboBox comboBoxDriver; + private JCheckBox checkBoxHomeAfterEnabled; + private JCheckBox checkBoxUsePickRotationInsteadOfRotationInTapeForStripFeeders; private String driverClassName; private JTextField discardXTf; private JTextField discardYTf; @@ -48,12 +52,21 @@ public ReferenceMachineConfigurationWizard(ReferenceMachine machine) { panelGeneral.setLayout(new FormLayout( new ColumnSpec[] {FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC, FormSpecs.RELATED_GAP_COLSPEC, FormSpecs.DEFAULT_COLSPEC,}, - new RowSpec[] {FormSpecs.RELATED_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC,})); + 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,})); JLabel lblDriver = new JLabel("Driver"); panelGeneral.add(lblDriver, "2, 2"); comboBoxDriver = new JComboBox(); - panelGeneral.add(comboBoxDriver, "4, 2"); + panelGeneral.add(comboBoxDriver, "2, 4"); + + checkBoxHomeAfterEnabled = new JCheckBox("Home after ENABLED?"); + panelGeneral.add(checkBoxHomeAfterEnabled, "2, 6"); + + checkBoxUsePickRotationInsteadOfRotationInTapeForStripFeeders = new JCheckBox("Use 'Pick Rotation' approach, instead of 'Rotation in tape', for Strip Feeders?"); + panelGeneral.add(checkBoxUsePickRotationInsteadOfRotationInTapeForStripFeeders, "2, 8"); comboBoxDriver.addItem(NullDriver.class.getCanonicalName()); comboBoxDriver.addItem(GcodeDriver.class.getCanonicalName()); @@ -123,6 +136,8 @@ public void createBindings() { LengthConverter lengthConverter = new LengthConverter(); addWrappedBinding(this, "driverClassName", comboBoxDriver, "selectedItem"); + addWrappedBinding(machine, "homeAfterEnabled", checkBoxHomeAfterEnabled, "selected"); + addWrappedBinding(machine, "usePickRotationInsteadOfRotationInTapeForStripFeeders", checkBoxUsePickRotationInsteadOfRotationInTapeForStripFeeders, "selected"); MutableLocationProxy discardLocation = new MutableLocationProxy(); bind(UpdateStrategy.READ_WRITE, machine, "discardLocation", discardLocation, "location"); diff --git a/src/main/java/org/openpnp/spi/Machine.java b/src/main/java/org/openpnp/spi/Machine.java index 671f4ce1a30..de57329b8ec 100644 --- a/src/main/java/org/openpnp/spi/Machine.java +++ b/src/main/java/org/openpnp/spi/Machine.java @@ -161,6 +161,10 @@ public interface Machine extends WizardConfigurable, PropertySheetHolder, Closea public Future submit(final Callable callable, final FutureCallback callback); + public boolean getHomeAfterEnabled(); + + public boolean getUsePickRotationInsteadOfRotationInTapeForStripFeeders(); + /** * Submit a task to be run with access to the Machine. This is the primary entry point into * executing any blocking operation on the Machine. If you are doing anything that results in diff --git a/src/main/java/org/openpnp/spi/PnpJobProcessor.java b/src/main/java/org/openpnp/spi/PnpJobProcessor.java index 06e6e4d61a4..7b6c2928d20 100644 --- a/src/main/java/org/openpnp/spi/PnpJobProcessor.java +++ b/src/main/java/org/openpnp/spi/PnpJobProcessor.java @@ -34,6 +34,10 @@ public double getPartHeight() { .getValue(); } + public String getPartId() { + return placement.getPart().getId(); + } + @Override public String toString() { return placement.getId();