/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3 as published by
the Free Software Foundation.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.cirqwizard.fx.pp;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import org.cirqwizard.fx.Context;
import org.cirqwizard.fx.ScreenController;
import org.cirqwizard.fx.controls.RealNumberTextField;
import org.cirqwizard.generation.toolpath.PPPoint;
import org.cirqwizard.layers.Board;
import org.cirqwizard.pp.ComponentId;
import org.cirqwizard.settings.*;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
public class ComponentPlacement extends ScreenController implements Initializable
{
@FXML private Label header;
@FXML private ComboBox<PPPoint> componentName;
@FXML private TitledPane pickupPane;
@FXML private RealNumberTextField pickupX;
@FXML private RealNumberTextField pickupY;
@FXML private Button gotoPickupButton;
@FXML private Button pickupButton;
@FXML private Button gotoTargetButton;
@FXML private Button pickupNGoButton;
@FXML private TitledPane placementPane;
@FXML private RealNumberTextField placementX;
@FXML private RealNumberTextField placementY;
@FXML private RealNumberTextField placementAngle;
@FXML private RealNumberTextField manualZ;
@FXML private RealNumberTextField targetX;
@FXML private RealNumberTextField targetY;
@FXML private RealNumberTextField targetAngle;
@FXML private Button moveHeadAwayButton;
@FXML private Button vacuumOffButton;
private ObservableList<PPPoint> components;
private HashMap<Integer, Integer[]> placementOffsets = new HashMap<>();
private static final int feederOffsetX = 10 * ApplicationConstants.RESOLUTION;
private static final int feederOffsetY = -15 * ApplicationConstants.RESOLUTION;
private static final DecimalFormat coordinatesFormat = new DecimalFormat("0.00");
private boolean atPickupLocation = false;
private Integer placementZ;
@Override
protected String getFxmlName()
{
return "ComponentPlacement.fxml";
}
@Override
protected String getName()
{
return "Placement";
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle)
{
components = FXCollections.observableArrayList();
componentName.setItems(components);
componentName.valueProperty().addListener((v, oldV, newV) -> updateComponent());
KeyboardHandler keyboardHandler = new KeyboardHandler();
pickupX.addEventFilter(KeyEvent.KEY_PRESSED, keyboardHandler);
pickupY.addEventFilter(KeyEvent.KEY_PRESSED, keyboardHandler);
placementX.addEventFilter(KeyEvent.KEY_PRESSED, keyboardHandler);
placementY.addEventFilter(KeyEvent.KEY_PRESSED, keyboardHandler);
placementAngle.addEventFilter(KeyEvent.KEY_PRESSED, keyboardHandler);
manualZ.addEventFilter(KeyEvent.KEY_PRESSED, keyboardHandler);
}
private class KeyboardHandler implements EventHandler<KeyEvent>
{
@Override
public void handle(KeyEvent event)
{
if (event.getCode() != KeyCode.UP && event.getCode() != KeyCode.DOWN)
return;
RealNumberTextField textField = (RealNumberTextField) event.getSource();
int currentValue = textField.getIntegerValue() == null ? 0 : textField.getIntegerValue();
int delta = textField == placementAngle ? 1000 : 100;
if (event.getCode() == KeyCode.DOWN)
delta = -delta;
textField.setIntegerValue(currentValue + delta);
textField.fireEvent(new ActionEvent());
}
}
@Override
public void refresh()
{
Context context = getMainApplication().getContext();
ComponentId id = context.getCurrentComponent();
header.setText(id.getPackaging() + " " + id.getValue());
components.setAll(context.getPanel().getCombinedElements(Board.LayerType.PLACEMENT).stream().
map(c -> (PPPoint)c).
filter(p -> p.getId().equals(id)).collect(Collectors.toList()));
componentName.getSelectionModel().select(0);
pickupNGoButton.setDisable(true);
placementPane.setDisable(true);
manualZ.setDisable(true);
MachineSettings machineSettings = SettingsFactory.getMachineSettings();
boolean referencePinsDefined = machineSettings.getReferencePinX().getValue() != null &&
machineSettings.getReferencePinY().getValue() != null;
if (referencePinsDefined)
{
pickupX.setIntegerValue(machineSettings.getReferencePinX().getValue() + feederOffsetX +
context.getComponentPitch() / 2);
pickupY.setIntegerValue(context.getFeeder().getYForRow(machineSettings.getReferencePinY().getValue() +
feederOffsetY, context.getFeederRow()));
}
gotoTargetButton.setDisable(true);
boolean disableOperation = getMainApplication().getCNCController() == null || !referencePinsDefined;
gotoPickupButton.setDisable(disableOperation);
pickupButton.setDisable(disableOperation);
moveHeadAwayButton.setDisable(disableOperation);
vacuumOffButton.setDisable(disableOperation);
}
private void updateComponent()
{
PPPoint p = componentName.getSelectionModel().getSelectedItem();
targetX.setIntegerValue(p.getPoint().getX());
targetY.setIntegerValue(p.getPoint().getY());
targetAngle.setIntegerValue(p.getAngle());
Integer[] offsets = placementOffsets.get(p.getAngle());
if (offsets != null)
{
placementX.setIntegerValue(offsets[0]);
placementY.setIntegerValue(offsets[1]);
placementAngle.setIntegerValue(offsets[2]);
}
else
{
placementX.setIntegerValue(null);
placementY.setIntegerValue(null);
placementAngle.setIntegerValue(null);
}
pickupPane.setDisable(false);
atPickupLocation = false;
}
private void rotatePP(int angle)
{
getMainApplication().getCNCController().rotatePP(angle, SettingsFactory.getPpSettings().getRotationFeed().getValue());
}
public void gotoPickup()
{
Integer moveHeight = SettingsFactory.getPpSettings().getMoveHeight().getValue();
getMainApplication().getCNCController().moveTo(pickupX.getIntegerValue(), pickupY.getIntegerValue(),
moveHeight);
rotatePP(0);
manualZ.setDisable(false);
manualZ.setIntegerValue(moveHeight);
atPickupLocation = true;
}
public void pickup()
{
PPSettings settings = SettingsFactory.getPpSettings();
Integer moveHeight = settings.getMoveHeight().getValue();
if (!atPickupLocation)
{
getMainApplication().getCNCController().moveTo(pickupX.getIntegerValue(), pickupY.getIntegerValue(),
moveHeight);
rotatePP(0);
}
getMainApplication().getCNCController().pickup(settings.getPickupHeight().getValue(), moveHeight);
manualZ.setDisable(false);
manualZ.setIntegerValue(moveHeight);
gotoTargetButton.setDisable(false);
}
private int getTargetX()
{
int x = targetX.getIntegerValue() + getMainApplication().getContext().getG54X();
if (placementX.getIntegerValue() != null)
x += placementX.getIntegerValue();
return x;
}
private int getTargetY()
{
int y = targetY.getIntegerValue() + getMainApplication().getContext().getG54Y();
if (placementY.getIntegerValue() != null)
y += placementY.getIntegerValue();
return y;
}
private int getTargetAngle()
{
int angle = targetAngle.getIntegerValue();
if (placementAngle.getIntegerValue() != null)
angle += placementAngle.getIntegerValue();
angle -= 90 * ApplicationConstants.RESOLUTION;
angle += SettingsFactory.getImportSettings().getCentroidAngularOffset().getValue() * ApplicationConstants.RESOLUTION;
return angle;
}
public void gotoTarget()
{
Integer moveHeight = SettingsFactory.getPpSettings().getMoveHeight().getValue();
getMainApplication().getCNCController().moveTo(getTargetX(), getTargetY(),
moveHeight);
rotatePP(getTargetAngle());
manualZ.setDisable(false);
manualZ.setIntegerValue(moveHeight);
pickupPane.setDisable(true);
placementPane.setDisable(false);
}
public void pickupAndGo()
{
gotoPickup();
pickup();
gotoTarget();
if (placementZ != null)
{
manualZ.setIntegerValue(placementZ);
manualZ.fireEvent(new ActionEvent());
}
}
public void place()
{
PPSettings settings = SettingsFactory.getPpSettings();
int z = settings.getPickupHeight().getValue() - 3 * ApplicationConstants.RESOLUTION;
getMainApplication().getCNCController().place(z, settings.getMoveHeight().getValue());
manualZ.setDisable(false);
manualZ.setIntegerValue(settings.getMoveHeight().getValue());
placementPane.setDisable(true);
gotoTargetButton.setDisable(true);
int selectedIndex = componentName.getSelectionModel().getSelectedIndex();
if (selectedIndex == componentName.getItems().size() - 1)
pickupPane.setDisable(true);
else
{
componentName.getSelectionModel().select(selectedIndex + 1);
pickupX.setIntegerValue(pickupX.getIntegerValue() + getMainApplication().getContext().getComponentPitch());
pickupNGoButton.setDisable(false);
}
}
public void moveHeadAway()
{
PredefinedLocationSettings settings = SettingsFactory.getPredefinedLocationSettings();
getMainApplication().getCNCController().moveTo(settings.getFarAwayX().getValue(),
settings.getFarAwayY().getValue(), settings.getFarAwayZ().getValue());
}
public void vacuumOff()
{
getMainApplication().getCNCController().vacuumOff();
placementPane.setDisable(true);
pickupPane.setDisable(false);
}
public void pickupLocationUpdated()
{
if (pickupX.getIntegerValue() == null || pickupY.getIntegerValue() == null)
return;
getMainApplication().getCNCController().moveTo(pickupX.getIntegerValue(), pickupY.getIntegerValue(), null);
atPickupLocation = true;
}
public void placementLocationUpdated()
{
getMainApplication().getCNCController().moveTo(getTargetX(), getTargetY(), null);
rotatePP(getTargetAngle());
placementOffsets.put(targetAngle.getIntegerValue(),
new Integer[]{placementX.getIntegerValue(), placementY.getIntegerValue(), placementAngle.getIntegerValue()});
}
public void manualZUpdated()
{
if (!placementPane.isDisabled())
placementZ = manualZ.getIntegerValue();
getMainApplication().getCNCController().moveZ(manualZ.getIntegerValue());
}
}