Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
f99985dc8This file is pinned to the selected release. Download the original to open it in your design tool.
package com.marginallyclever.makelangelo.plotter.plottercontrols;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.marginallyclever.communications.NetworkSessionEvent;
import com.marginallyclever.convenience.ButtonIcon;
import com.marginallyclever.convenience.CommandLineOptions;
import com.marginallyclever.makelangelo.CollapsiblePanel;
import com.marginallyclever.makelangelo.Translator;
import com.marginallyclever.makelangelo.plotter.Plotter;
import com.marginallyclever.makelangelo.plotter.PlotterEvent;
import com.marginallyclever.makelangelo.plotter.plottersettings.PlotterSettings;
import com.marginallyclever.makelangelo.turtle.Turtle;
import com.marginallyclever.util.PreferencesHelper;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.util.Arrays;
/**
* {@link PlotterControls} brings together three separate panels and wraps all
* the lower level features in a human friendly, intuitive interface. - The
* {@link MarlinPanel}, which manages the two way network connection to a
* robot running Marlin firmware. - The {@link JogPanel}, which is a
* human-friendly way to drive a {@link Plotter} - The {@link ProgramPanel},
* which is a buffer for queueing commands to a {@link Plotter}
*
* @author Dan Royer
* @since 7.28.0
*/
public class PlotterControls extends JPanel {
public static final int DIMENSION_PANEL_WIDTH = 850;
public static final int DIMENSION_PANEL_HEIGHT = 510;
private static final int DIMENSION_COLLAPSIBLE_HEIGHT = 570;
private final Plotter myPlotter;
private final Turtle myTurtle;
private final JogPanel jogPanel;
private final MarlinPlotterPanel marlinInterface;
private final ProgramPanel programPanel;
private final ChooseConnection chooseConnection = new ChooseConnection();
private ButtonIcon bFindHome;
private ButtonIcon bRewind;
private ButtonIcon bStart;
private ButtonIcon bStep;
private ButtonIcon bPause;
private ButtonIcon bEmergencyStop;
private final JProgressBar progress = new JProgressBar(0, 100);
private boolean isRunning = false;
private boolean penIsUpBeforePause = false;
private boolean isErrorAlreadyDisplayed = false;
public PlotterControls(Plotter plotter, Turtle turtle, Window parentWindow) {
super();
myPlotter = plotter;
myTurtle = turtle;
jogPanel = new JogPanel(plotter);
marlinInterface = new MarlinPlotterPanel(plotter, chooseConnection);
programPanel = new ProgramPanel(plotter, turtle);
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab(Translator.get("PlotterControls.JogTab"), jogPanel);
tabbedPane.addTab(Translator.get("PlotterControls.MarlinTab"), marlinInterface);
tabbedPane.addTab(Translator.get("PlotterControls.ProgramTab"), programPanel);
CollapsiblePanel collapsiblePanel = new CollapsiblePanel(parentWindow, Translator.get("PlotterControls.AdvancedControls"), DIMENSION_COLLAPSIBLE_HEIGHT, false);
collapsiblePanel.add(tabbedPane);
this.setLayout(new BorderLayout());
this.add(collapsiblePanel, BorderLayout.CENTER);
this.add(getButtonsPanels(), BorderLayout.NORTH);
this.add(progress, BorderLayout.SOUTH);
marlinInterface.addListener(this::onMarlinEvent);
myPlotter.addPlotterEventListener((e)-> {
if (e.type == PlotterEvent.HOME_FOUND) {
updateButtonStatusConnected();
}
});
}
private void onMarlinEvent(MarlinPanelEvent e) {
switch (e.getID()) {
case MarlinPanelEvent.IDLE -> {
if (isRunning) step();
}
case MarlinPanelEvent.ERROR,
MarlinPanelEvent.DID_NOT_FIND,
MarlinPanelEvent.COMMUNICATION_FAILURE -> {
if (!isErrorAlreadyDisplayed) {
String message;
switch(e.getActionCommand()) {
case "communicationFailure" -> message = Translator.get("PlotterControls.communicationFailure");
case "didNotFind" -> message = Translator.get("PlotterControls.didNotFind");
case "Printed halted" -> message = Translator.get("PlotterControls.halted");
default -> message = e.getActionCommand();
}
/* TODO Source of dialog box titled "Error" that says "PlotterControls.null".
* Caused by robot being turned off while COM port is connected.
*/
JOptionPane.showMessageDialog(this,
message,
Translator.get("ErrorTitle"),
JOptionPane.ERROR_MESSAGE);
isErrorAlreadyDisplayed = true;
}
}
case MarlinPanelEvent.HOME_XY_FIRST ->
JOptionPane.showMessageDialog(this,
Translator.get("PlotterControls.homeXYFirst"),
Translator.get("InfoTitle"),
JOptionPane.WARNING_MESSAGE);
}
updateProgressBar();
}
private JPanel getButtonsPanels() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(getConnectPanel(), BorderLayout.NORTH);
panel.add(getDrawPanel(), BorderLayout.CENTER);
return panel;
}
private JPanel getConnectPanel() {
JPanel panel = new JPanel();
Border border = BorderFactory.createTitledBorder(Translator.get("PlotterControls.ConnectControls"));
panel.setBorder(border);
panel.add(chooseConnection);
chooseConnection.addListener(e -> {
switch (e.flag) {
case NetworkSessionEvent.CONNECTION_OPENED -> onConnect();
case NetworkSessionEvent.CONNECTION_CLOSED -> onDisconnect();
}
});
return panel;
}
private JPanel getDrawPanel() {
JPanel panel = new JPanel();
Border border = BorderFactory.createTitledBorder(Translator.get("PlotterControls.DrawControls"));
panel.setBorder(border);
bFindHome = new ButtonIcon( Translator.get("PlotterControls.FindHome"), "/images/house.png");
bRewind = new ButtonIcon( Translator.get("PlotterControls.Rewind"), "/images/control_start_blue.png");
bStart = new ButtonIcon( Translator.get("PlotterControls.Play"), "/images/control_play_blue.png");
bStep = new ButtonIcon( Translator.get("PlotterControls.Step"), "/images/control_fastforward_blue.png");
bPause = new ButtonIcon( Translator.get("PlotterControls.Pause"), "/images/control_pause_blue.png");
bEmergencyStop = new ButtonIcon( Translator.get("PlotterControls.EmergencyStop"), "/images/stop.png");
bEmergencyStop.setForeground(Color.RED);
panel.add(bFindHome);
panel.add(bRewind);
panel.add(bStart);
panel.add(bPause);
panel.add(bStep);
panel.add(bEmergencyStop);
bFindHome.addActionListener(e -> findHome());
bRewind.addActionListener(e -> rewind());
bStart.addActionListener(e -> play());
bPause.addActionListener(e -> pause());
bStep.addActionListener(e -> step());
bEmergencyStop.addActionListener(e ->
{
marlinInterface.sendESTOP();
chooseConnection.closeConnection();
});
onDisconnect();
return panel;
}
private void findHome() {
jogPanel.findHome();
updateButtonStatusConnected();
}
private void step() {
programPanel.step();
if (programPanel.getLineNumber() == -1) {
// done
addUserEndGCODE();
pause();
}
}
public void startAt(int lineNumber) {
int count = programPanel.getMoveCount();
if (lineNumber >= count)
lineNumber = count;
if (lineNumber < 0)
lineNumber = 0;
programPanel.setLineNumber(lineNumber);
play();
}
private void updateProgressBar() {
progress.setValue((int) (100.0 * programPanel.getLineNumber() / programPanel.getMoveCount()));
}
private void rewind() {
programPanel.rewind();
progress.setValue(0);
}
private void play() {
isRunning = true;
updateButtonStatusConnected();
if (!penIsUpBeforePause)
myPlotter.lowerPen();
rewindIfNoProgramLineSelected();
addUserStartGCODE();
step();
}
private void rewindIfNoProgramLineSelected() {
if (programPanel.getLineNumber() == -1) {
programPanel.rewind();
}
}
private void pause() {
isRunning = false;
updateButtonStatusConnected();
penIsUpBeforePause = myPlotter.getPenIsUp();
if (!penIsUpBeforePause)
myPlotter.raisePen();
}
public boolean isRunning() {
return isRunning;
}
private void updateButtonStatusConnected() {
boolean isHomed = myPlotter.getDidFindHome();
bRewind.setEnabled(isHomed && !isRunning);
bStart.setEnabled(isHomed && !isRunning);
bPause.setEnabled(isHomed && isRunning);
bStep.setEnabled(isHomed && !isRunning);
}
private void onConnect() {
myPlotter.reInit();
bFindHome.setEnabled(true);
bEmergencyStop.setEnabled(true);
isRunning = false;
updateButtonStatusConnected();
jogPanel.onNetworkConnect();
isErrorAlreadyDisplayed = false;
}
private void onDisconnect() {
myPlotter.reInit();
bFindHome.setEnabled(false);
bEmergencyStop.setEnabled(false);
bRewind.setEnabled(false);
bStart.setEnabled(false);
bPause.setEnabled(false);
bStep.setEnabled(false);
jogPanel.onNetworkDisconnect();
}
@SuppressWarnings("unused")
private int findLastPenUpBefore(int startAtLine) {
int sum=0;
for( var strokeLayer : myTurtle.getLayers() ) {
for (var line : strokeLayer.getAllLines()) {
int size = line.getAllPoints().size();
if (sum <= startAtLine && startAtLine < sum + size) {
// the start of this line (sum) is the right value.
return sum;
}
}
}
// all layers are empty?
return 0;
}
private void addUserStartGCODE() {
String gcode = myPlotter.getSettings().getString(PlotterSettings.START_GCODE);
Arrays.asList(gcode.split(System.getProperty("line.separator")).clone())
.forEach(marlinInterface::queueAndSendCommands);
}
private void addUserEndGCODE() {
String gcode = myPlotter.getSettings().getString(PlotterSettings.END_GCODE);
Arrays.asList(gcode.split(System.getProperty("line.separator")).clone())
.forEach(marlinInterface::queueAndSendCommands);
}
/**
* Called from windowAdapter::windowClosing() to clean up resources.
*/
public void onDialogClosing() {
// make sure to close the connection when the dialog is closed.
chooseConnection.closeConnection();
// make sure to unregister listeners
marlinInterface.stopListeningToPlotter();
}
// TEST
public static void main(String[] args) throws UnsupportedLookAndFeelException {
PreferencesHelper.start();
CommandLineOptions.setFromMain(args);
Translator.start();
FlatLaf.registerCustomDefaultsSource( "com.marginallyclever.makelangelo" );
UIManager.setLookAndFeel( new FlatLightLaf() );
JFrame frame = new JFrame(Translator.get("PlotterControls.Title"));
frame.setPreferredSize(new Dimension(DIMENSION_PANEL_WIDTH, DIMENSION_PANEL_HEIGHT));
frame.setMinimumSize(new Dimension(DIMENSION_PANEL_WIDTH, DIMENSION_PANEL_HEIGHT));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PlotterControls(new Plotter(), new Turtle(), frame));
frame.pack();
frame.setVisible(true);
}
}