diff --git a/ugs-core/src/com/willwinder/universalgcodesender/AbstractCommunicator.java b/ugs-core/src/com/willwinder/universalgcodesender/AbstractCommunicator.java index 895fc0a04d..c9e81e3bce 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/AbstractCommunicator.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/AbstractCommunicator.java @@ -74,6 +74,21 @@ public AbstractCommunicator() { /*********************/ abstract public void setSingleStepMode(boolean enable); abstract public boolean getSingleStepMode(); + + /** + * Enable singleBlockMode, pausing the commandStream after every + * block sent to the controller. + * @param boolean enable true if enabled + */ + abstract public void setSingleBlockMode(boolean enable); + + /** + * Get the state of singleBlockMode, which pauses the commandStream + * after every block sent to the controller. + * @return boolean true if enabled + */ + abstract public boolean getSingleBlockMode(); + abstract public void queueStringForComm(final String input); abstract public void queueStreamForComm(final GcodeStreamReader input); abstract public void sendByteImmediately(byte b) throws Exception; diff --git a/ugs-core/src/com/willwinder/universalgcodesender/AbstractController.java b/ugs-core/src/com/willwinder/universalgcodesender/AbstractController.java index 486b390513..1ce60d5ef6 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/AbstractController.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/AbstractController.java @@ -347,6 +347,32 @@ public boolean getSingleStepMode() { } return false; } + + /** + * Wrapper to set singleBlockMode in the communicator + * @see AbstractCommunicator.setSingleBlockMode() + * @param boolean true to enable + */ + @Override + public void setSingleBlockMode(boolean enabled) { + if (this.comm != null) { + this.comm.setSingleBlockMode(enabled); + } + } + + /** + * Wrapper to get the singleBlockMode in the communicator. + * getSingleBlockMode wrapper for AbstractCommunicator + * @see AbstractCommunicator.getSingleBlockMode() + * @return boolean true if enabled else false + */ + @Override + public boolean getSingleBlockMode() { + if (this.comm != null) { + return this.comm.getSingleBlockMode(); + } + return false; + } @Override public void setStatusUpdatesEnabled(boolean enabled) { diff --git a/ugs-core/src/com/willwinder/universalgcodesender/BufferedCommunicator.java b/ugs-core/src/com/willwinder/universalgcodesender/BufferedCommunicator.java index 427c329036..7f96c3d3e3 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/BufferedCommunicator.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/BufferedCommunicator.java @@ -47,6 +47,7 @@ public abstract class BufferedCommunicator extends AbstractCommunicator { private int sentBufferSize = 0; private Boolean singleStepModeEnabled = false; + private Boolean singleBlockModeEnabled = false; abstract public int getBufferSize(); @@ -69,6 +70,16 @@ public void setSingleStepMode(boolean enable) { public boolean getSingleStepMode() { return this.singleStepModeEnabled; } + + @Override + public void setSingleBlockMode(boolean enable) { + this.singleBlockModeEnabled = enable; + } + + @Override + public boolean getSingleBlockMode() { + return this.singleBlockModeEnabled; + } /** * Add command to the command queue outside file mode. This is the only way @@ -187,6 +198,39 @@ else if (!this.commandBuffer.isEmpty()) { } return null; } + + /** + * See the next command to be sent out without popping from the + * commandBuffer or commandStream. + * @see BufferedCommunicator.getNextCommand() + * @see GcodeStreamReader.peekNextCommand() + * @return GcodeCommand the next command to be sent to the controller + */ + private GcodeCommand peekNextCommand() { + GcodeCommand nc = null; + + if (nextCommand != null) { + nc = nextCommand; + } + else if (!this.commandBuffer.isEmpty()) { + nc = new GcodeCommand(commandBuffer.peek()); + } + else + try { + if (commandStream != null && commandStream.ready()) + { + nc = commandStream.peekNextCommand(); + } + } catch (IOException ignored) { + // Fall through to null handling. + } + + if (nc != null && nc.getCommandString().endsWith("\n")) { + nc.setCommand(nextCommand.getCommandString().trim()); + } + + return nc; + } /** * Streams anything in the command buffer to the comm port. @@ -235,6 +279,18 @@ && allowMoreCommands()) { e.printStackTrace(); System.exit(-1); } + + // Single block mode: pause after this command is sent + // iff the next command is from the commandStream + // (ie do not pause for commandBuffer commands) + if( this.getSingleBlockMode() && + commandBuffer.isEmpty() && + peekNextCommand() != null && + !peekNextCommand().getCommandString().isEmpty() ) { + + logger.log(Level.INFO, "singleBlockMode: pausing"); + pauseSend(); + } } } diff --git a/ugs-core/src/com/willwinder/universalgcodesender/IController.java b/ugs-core/src/com/willwinder/universalgcodesender/IController.java index c17d30be09..c7fc283429 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/IController.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/IController.java @@ -127,6 +127,21 @@ void jogMachine(int dirX, int dirY, int dirZ, void setSingleStepMode(boolean enabled); boolean getSingleStepMode(); + /** + * Wrapper to set singleBlockMode in the communicator + * @see AbstractCommunicator.setSingleBlockMode() + * @param boolean true to enable + */ + void setSingleBlockMode(boolean enabled); + + /** + * Wrapper to get the singleBlockMode in the communicator. + * getSingleBlockMode wrapper for AbstractCommunicator + * @see AbstractCommunicator.getSingleBlockMode() + * @return boolean true if enabled else false + */ + boolean getSingleBlockMode(); + void setStatusUpdatesEnabled(boolean enabled); boolean getStatusUpdatesEnabled(); diff --git a/ugs-core/src/com/willwinder/universalgcodesender/model/GUIBackend.java b/ugs-core/src/com/willwinder/universalgcodesender/model/GUIBackend.java index d2725b1613..5ebe8bf9f2 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/model/GUIBackend.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/model/GUIBackend.java @@ -771,6 +771,7 @@ public void applySettingsToController(Settings settings, IController controller) try { controller.setSingleStepMode(settings.isSingleStepMode()); + controller.setSingleBlockMode(settings.isSingleBlockMode()); controller.setStatusUpdatesEnabled(settings.isStatusUpdatesEnabled()); controller.setStatusUpdateRate(settings.getStatusUpdateRate()); } catch (Exception ex) { diff --git a/ugs-core/src/com/willwinder/universalgcodesender/uielements/panels/ConnectionSettingsPanel.java b/ugs-core/src/com/willwinder/universalgcodesender/uielements/panels/ConnectionSettingsPanel.java index bcc2c8248e..d55c7dbd88 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/uielements/panels/ConnectionSettingsPanel.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/uielements/panels/ConnectionSettingsPanel.java @@ -46,6 +46,8 @@ public class ConnectionSettingsPanel extends AbstractUGSSettings { Localization.getString("sender.step.separateZ")); private final Checkbox singleStepMode = new Checkbox( Localization.getString("sender.singlestep")); + private final Checkbox singleBlockMode = new Checkbox( + Localization.getString("sender.singleblock")); private final Checkbox statusPollingEnabled = new Checkbox( Localization.getString("sender.status")); private final Spinner statusPollRate = new Spinner( @@ -77,6 +79,7 @@ public ConnectionSettingsPanel(Settings settings) { public String getHelpMessage() { return Localization.getString("sender.help.verbose.console") + "\n\n" + Localization.getString("sender.help.singlestep") + "\n\n" + + Localization.getString("sender.help.singleblock") + "\n\n" + Localization.getString("sender.help.status") + "\n\n" + Localization.getString("sender.help.status.rate") + "\n\n" + Localization.getString("sender.help.state") + "\n\n"; @@ -87,6 +90,7 @@ public void save() { settings.setVerboseOutputEnabled(verboseConsoleOutput.getValue()); settings.setUseZStepSize(useZStepSize.getValue()); settings.setSingleStepMode(singleStepMode.getValue()); + settings.setSingleBlockMode(singleBlockMode.getValue()); settings.setStatusUpdatesEnabled(statusPollingEnabled.getValue()); settings.setStatusUpdateRate((int)statusPollRate.getValue()); //settings.setAutoConnectEnabled(autoConnect.getValue()); @@ -125,6 +129,9 @@ protected void updateComponentsInternal(Settings s) { singleStepMode.setSelected(s.isSingleStepMode()); add(singleStepMode, "spanx, wrap"); + singleBlockMode.setSelected(s.isSingleBlockMode()); + add(singleBlockMode, "spanx, wrap"); + statusPollingEnabled.setSelected(s.isStatusUpdatesEnabled()); add(statusPollingEnabled, "spanx, wrap"); diff --git a/ugs-core/src/com/willwinder/universalgcodesender/utils/GcodeStreamReader.java b/ugs-core/src/com/willwinder/universalgcodesender/utils/GcodeStreamReader.java index bcf76ddaf8..c9b8ea03b7 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/utils/GcodeStreamReader.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/utils/GcodeStreamReader.java @@ -98,6 +98,31 @@ public GcodeCommand getNextCommand() throws IOException { Integer.parseInt(nextLine[COL_COMMAND_NUMBER])); } + /** + * Peek at the next command without moving the pointers + * NOTE: this calls mark() and reset() so reset() calls outside of + * peekNextCommand() may be interfered with by this. + * @return GcodeCommand containing next line + * @throws IOException + */ + public GcodeCommand peekNextCommand() throws IOException { + if (numRowsRemaining == 0) return null; + + reader.mark(NUM_COLUMNS); // bookmark for rollback + String line = reader.readLine(); + reader.reset(); // rollback to mark + + String nextLine[] = parseLine(line); + if (nextLine.length != NUM_COLUMNS) { + throw new IOException("Corrupt data found while processing gcode stream: " + line); + } + return new GcodeCommand( + nextLine[COL_PROCESSED_COMMAND], + nextLine[COL_ORIGINAL_COMMAND], + nextLine[COL_COMMENT], + Integer.parseInt(nextLine[COL_COMMAND_NUMBER])); + } + @Override public void close() throws IOException { reader.close(); diff --git a/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java b/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java index 141a3d26f9..f3af143d84 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/utils/Settings.java @@ -37,6 +37,7 @@ This file is part of Universal Gcode Sender (UGS). import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.logging.Level; import java.util.logging.Logger; public class Settings { @@ -64,6 +65,7 @@ public class Settings { private WindowSettings mainWindowSettings = new WindowSettings(0,0,640,520); private WindowSettings visualizerWindowSettings = new WindowSettings(0,0,640,480); private boolean singleStepMode = false; + private boolean singleBlockMode = false; private boolean statusUpdatesEnabled = true; private int statusUpdateRate = 200; private Units preferredUnits = Units.MM; @@ -313,6 +315,29 @@ public void setSingleStepMode(boolean singleStepMode) { changed(); } + /** + * Retrieve the setting to determine if communicator should pause + * streaming after each command in the communicator commandStream. + * @see AbstractCommunicator.getSingleBlockMode() + * @return true if singleBlockMode is enabled + */ + public boolean isSingleBlockMode() { + return singleBlockMode; + } + + /** + * Enable / disable the pausing of streaming after each command in + * the commandStream of the communicator. Also requires + * singleStepMode to be enabled + * @see AbstractCommunicator.setSingleBlockMode() + * @param boolean singleBlockMode true if enabled + */ + public void setSingleBlockMode(boolean singleBlockMode) { + logger.log(Level.INFO, "Setting singleBlockMode to ".concat( ((Boolean)singleBlockMode).toString() )); + this.singleBlockMode = singleBlockMode; + changed(); + } + public boolean isStatusUpdatesEnabled() { return statusUpdatesEnabled; } diff --git a/ugs-core/src/com/willwinder/universalgcodesender/utils/SettingsFactory.java b/ugs-core/src/com/willwinder/universalgcodesender/utils/SettingsFactory.java index 49a97e2794..04b62941c1 100644 --- a/ugs-core/src/com/willwinder/universalgcodesender/utils/SettingsFactory.java +++ b/ugs-core/src/com/willwinder/universalgcodesender/utils/SettingsFactory.java @@ -136,6 +136,7 @@ private static void migrateOldSettings() { out.setVerboseOutputEnabled(Boolean.valueOf(properties.getProperty("verboseOutput.enabled", FALSE))); out.setFirmwareVersion(properties.getProperty("firmwareVersion", "GRBL")); out.setSingleStepMode(Boolean.valueOf(properties.getProperty("singleStepMode", FALSE))); + out.setSingleBlockMode(Boolean.valueOf(properties.getProperty("singleBlockMode", FALSE))); out.setStatusUpdatesEnabled(Boolean.valueOf(properties.getProperty("statusUpdatesEnabled", "true"))); out.setStatusUpdateRate(Integer.valueOf(properties.getProperty("statusUpdateRate", "200"))); out.updateMacro(1, null, null, properties.getProperty("customGcode1", "G0 X0 Y0;")); diff --git a/ugs-core/src/resources/MessagesBundle_en_US.properties b/ugs-core/src/resources/MessagesBundle_en_US.properties index fc7999e32c..097f66a673 100644 --- a/ugs-core/src/resources/MessagesBundle_en_US.properties +++ b/ugs-core/src/resources/MessagesBundle_en_US.properties @@ -163,6 +163,7 @@ sender.speed.percent = Speed override percent sender.command.length = Max command length sender.truncate = Truncate decimal digits sender.singlestep = Enable single step mode +sender.singleblock = Enable single block mode sender.whitespace = Remove all whitespace in commands sender.status = Enable status polling sender.status.rate = Status poll rate (ms) @@ -180,6 +181,7 @@ sender.help.speed.percent = Speed override percent\: Factor that speeds will be sender.help.command.length = Max command length\: Maximum length of a command before an error is triggered. sender.help.truncate = Truncate decimal digits\: Number of fractional digits that will be sent to firmware. sender.help.singlestep = Enable single step mode\: Turns on single step mode, this is very slow. +sender.help.singleblock = Enable single block mode\: Pause sender after every block, useful for testing programs though extremely slow. sender.help.whitespace = Remove all whitespace\: Removes the usually unnecessary whitespace in gcode commands. sender.help.status = Enable status polling\: Turns on status polling for firmware if supported. sender.help.status.rate = Status poll rate\: The rate in milliseconds that status requests are sent at. diff --git a/ugs-core/test/com/willwinder/universalgcodesender/BufferedCommunicatorTest.java b/ugs-core/test/com/willwinder/universalgcodesender/BufferedCommunicatorTest.java index cdeca8bff7..0034cc9497 100644 --- a/ugs-core/test/com/willwinder/universalgcodesender/BufferedCommunicatorTest.java +++ b/ugs-core/test/com/willwinder/universalgcodesender/BufferedCommunicatorTest.java @@ -101,6 +101,16 @@ public void testSingleStepMode() { fail("Not implemented yet."); } + /** + * Test of setSingleBlockMode method of class BufferedCommunicator. + */ + @Test + @Ignore + public void testSingleBlockMode() { + System.out.println("testSingleBlockMode"); + fail("Not implemented yet."); + } + /** * Test of getBufferSize method, of class BufferedCommunicator. */ diff --git a/ugs-platform/ugs-platform-ugscore/src/main/java/com/willwinder/ugs/nbp/core/windows/DiagnosticsTopComponent.java b/ugs-platform/ugs-platform-ugscore/src/main/java/com/willwinder/ugs/nbp/core/windows/DiagnosticsTopComponent.java index 8dba760590..d3ecdf3ffb 100644 --- a/ugs-platform/ugs-platform-ugscore/src/main/java/com/willwinder/ugs/nbp/core/windows/DiagnosticsTopComponent.java +++ b/ugs-platform/ugs-platform-ugscore/src/main/java/com/willwinder/ugs/nbp/core/windows/DiagnosticsTopComponent.java @@ -95,6 +95,7 @@ private void initComponents() { this.labels.put("communicator:numActiveCommands", new JLabel("-----")); this.labels.put("communicator:isPaused", new JLabel("-----")); this.labels.put("communicator:getSingleStepMode", new JLabel("-----")); + this.labels.put("communicator:getSingleBlockMode", new JLabel("-----")); this.labels.put("settings:isHomingEnabled", new JLabel("-----")); this.labels.put("settings:getReportingUnits", new JLabel("-----"));