Skip to content

Commit

Permalink
Positive space capability (#2654)
Browse files Browse the repository at this point in the history
Add a capability for finding out if machine is working in positive space
  • Loading branch information
breiler authored Dec 8, 2024
1 parent 3f1b34c commit 19430be
Show file tree
Hide file tree
Showing 25 changed files with 479 additions and 217 deletions.
30 changes: 13 additions & 17 deletions ugs-core/src/com/willwinder/universalgcodesender/Capabilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,30 +181,21 @@ public boolean hasReturnToZero() {
return hasCapability(CapabilitiesConstants.RETURN_TO_ZERO);
}


/**
* Returns if the controller has support for the given axis
*
* @param axis - the axis to check support for
* @return true if the axis is supported
*/
public boolean hasAxis(Axis axis) {
switch (axis) {
case X:
return hasCapability(CapabilitiesConstants.X_AXIS);
case Y:
return hasCapability(CapabilitiesConstants.Y_AXIS);
case Z:
return hasCapability(CapabilitiesConstants.Z_AXIS);
case A:
return hasCapability(CapabilitiesConstants.A_AXIS);
case B:
return hasCapability(CapabilitiesConstants.B_AXIS);
case C:
return hasCapability(CapabilitiesConstants.C_AXIS);
default:
return false;
}
return switch (axis) {
case X -> hasCapability(CapabilitiesConstants.X_AXIS);
case Y -> hasCapability(CapabilitiesConstants.Y_AXIS);
case Z -> hasCapability(CapabilitiesConstants.Z_AXIS);
case A -> hasCapability(CapabilitiesConstants.A_AXIS);
case B -> hasCapability(CapabilitiesConstants.B_AXIS);
case C -> hasCapability(CapabilitiesConstants.C_AXIS);
};
}

/**
Expand All @@ -215,4 +206,9 @@ public boolean hasAxis(Axis axis) {
public boolean hasOpenDoor() {
return hasCapability(CapabilitiesConstants.OPEN_DOOR);
}

@Override
public String toString() {
return String.join(", ", capabilities);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ public class CapabilitiesConstants {
public static final String RETURN_TO_ZERO = "RETURN_TO_ZERO";

/**
* A key for identifying if the firmware supports opening the door
* A key for identifying if the firmware supports triggering the DOOR state
* from the sender.
*/
public static final String OPEN_DOOR = "DOOR_DOOR";
public static final String OPEN_DOOR = "OPEN_DOOR";

public static final String X_AXIS = "X_AXIS";
public static final String Y_AXIS = "Y_AXIS";
Expand All @@ -91,4 +92,21 @@ public class CapabilitiesConstants {
* A key for identifying if the firmware supports handling the device file system
*/
public static final String FILE_SYSTEM = "FILE_SYSTEM";

/**
* Traditionally CNC:s works in a negative machine space. When the machine is homed it is usually done
* in the right, far, upper corner which is then set to 0, therefore all coordinates in the machine space is
* defined with negative values.
* <p>
* Some controllers have the option to inverse this, making the machine zero at the left, lower, close corner of
* the machine.
* <p>
* By defining this capability we can account for this in the visualizer.
*/
public static final String MACHINE_POSITION_IN_POSITIVE_SPACE = "MACHINE_POSITION_IN_POSITIVE_SPACE";

/**
* Does the controller support variable spindles (typically PWM or through RS484 interfaces)
*/
public static final String VARIABLE_SPINDLE = "VARIABLE_SPINDLE";
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.communicator.ICommunicator;
import com.willwinder.universalgcodesender.connection.ConnectionDriver;
import com.willwinder.universalgcodesender.firmware.IFirmwareSettings;
import com.willwinder.universalgcodesender.firmware.grbl.GrblCapabilitiesConstants;
import com.willwinder.universalgcodesender.firmware.grbl.GrblCommandCreator;
import com.willwinder.universalgcodesender.firmware.grbl.GrblFirmwareSettings;
import com.willwinder.universalgcodesender.firmware.grbl.GrblFirmwareSettingsInterceptor;
Expand Down Expand Up @@ -192,7 +193,8 @@ private void initialize() {
return;
}

capabilities = GrblUtils.getGrblStatusCapabilities(initializer.getVersion().getVersionNumber(), initializer.getVersion().getVersionLetter());
capabilities = GrblUtils.getGrblStatusCapabilities(initializer.getVersion().getVersionNumber(), initializer.getVersion().getVersionLetter(), initializer.getOptions());
logger.info("Identified controller capabilities: " + capabilities);

// Toggle the state to force UI update
setControllerState(ControllerState.CONNECTING);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.willwinder.universalgcodesender;

import com.willwinder.universalgcodesender.firmware.grbl.GrblBuildOptions;
import com.willwinder.universalgcodesender.firmware.grbl.GrblVersion;
import com.willwinder.universalgcodesender.firmware.grbl.commands.GetBuildInfoCommand;
import com.willwinder.universalgcodesender.firmware.grbl.commands.GetParserStateCommand;
Expand Down Expand Up @@ -32,13 +33,14 @@ public class GrblControllerInitializer implements IControllerInitializer {
private final AtomicBoolean isInitialized = new AtomicBoolean(false);
private final GrblController controller;
private GrblVersion version = GrblVersion.NO_VERSION;
private GrblBuildOptions options = new GrblBuildOptions();

public GrblControllerInitializer(GrblController controller) {
this.controller = controller;
}

@Override
public boolean initialize() throws ControllerException{
public boolean initialize() throws ControllerException {
// Only allow one initialization at a time
if (isInitializing.get() || isInitialized.get()) {
return false;
Expand Down Expand Up @@ -100,6 +102,7 @@ private void fetchControllerVersion() throws InterruptedException {
}

version = optionalVersion.get();
options = getBuildInfoCommand.getBuildOptions();
}

@Override
Expand All @@ -121,4 +124,8 @@ public boolean isInitializing() {
public GrblVersion getVersion() {
return version;
}

public GrblBuildOptions getOptions() {
return options;
}
}
13 changes: 12 additions & 1 deletion ugs-core/src/com/willwinder/universalgcodesender/GrblUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ This file is part of Universal Gcode Sender (UGS).

package com.willwinder.universalgcodesender;

import com.willwinder.universalgcodesender.firmware.grbl.GrblCapabilitiesConstants;
import com.willwinder.universalgcodesender.firmware.grbl.GrblBuildOption;
import com.willwinder.universalgcodesender.firmware.grbl.GrblBuildOptions;
import com.willwinder.universalgcodesender.firmware.grbl.commands.GetStatusCommand;
import com.willwinder.universalgcodesender.firmware.grbl.commands.GrblSystemCommand;
import com.willwinder.universalgcodesender.listeners.AccessoryStates;
Expand Down Expand Up @@ -232,7 +235,7 @@ protected static String getViewParserStateCommand(final double version, final Ch
/**
* Determines version of GRBL position capability.
*/
protected static Capabilities getGrblStatusCapabilities(final double version, final Character letter) {
protected static Capabilities getGrblStatusCapabilities(final double version, final Character letter, GrblBuildOptions options) {
Capabilities ret = new Capabilities();
ret.addCapability(CapabilitiesConstants.JOGGING);
ret.addCapability(CapabilitiesConstants.CHECK_MODE);
Expand Down Expand Up @@ -266,6 +269,14 @@ protected static Capabilities getGrblStatusCapabilities(final double version, fi
ret.addCapability(CapabilitiesConstants.OPEN_DOOR);
}

if (options.isEnabled(GrblBuildOption.HOMING_FORCE_ORIGIN_ENABLED)) {
ret.addCapability(CapabilitiesConstants.MACHINE_POSITION_IN_POSITIVE_SPACE);
}

if (options.isEnabled(GrblBuildOption.VARIABLE_SPINDLE_ENABLED)) {
ret.addCapability(CapabilitiesConstants.VARIABLE_SPINDLE);
}

return ret;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.Capabilities;
import com.willwinder.universalgcodesender.ConnectionWatchTimer;
import com.willwinder.universalgcodesender.ControllerException;
import com.willwinder.universalgcodesender.GrblCapabilitiesConstants;
import com.willwinder.universalgcodesender.GrblUtils;
import com.willwinder.universalgcodesender.IController;
import com.willwinder.universalgcodesender.IFileService;
Expand All @@ -41,12 +40,13 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.DetectEchoCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.FluidNCCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetAlarmCodesCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetBuildInfoCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetErrorCodesCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetFirmwareVersionCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetParserStateCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetStartupMessagesCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetStatusCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.SystemCommand;
import com.willwinder.universalgcodesender.firmware.grbl.GrblCapabilitiesConstants;
import com.willwinder.universalgcodesender.firmware.grbl.GrblOverrideManager;
import com.willwinder.universalgcodesender.gcode.GcodeParser;
import com.willwinder.universalgcodesender.gcode.GcodeState;
Expand Down Expand Up @@ -582,9 +582,9 @@ private void disableEcho() throws Exception {
private void queryFirmwareVersion() throws Exception {
// A sleep is required to make the next query reliable
Thread.sleep(200);
GetFirmwareVersionCommand getFirmwareVersionCommand = FluidNCUtils.queryFirmwareVersion(this, messageService);
semanticVersion = getFirmwareVersionCommand.getVersion();
firmwareVariant = getFirmwareVersionCommand.getFirmware();
GetBuildInfoCommand getBuildInfoCommand = FluidNCUtils.queryBuildInformation(this, messageService);
semanticVersion = getBuildInfoCommand.getVersion();
firmwareVariant = getBuildInfoCommand.getFirmware();
capabilities.addCapability(GrblCapabilitiesConstants.V1_FORMAT);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.willwinder.universalgcodesender.firmware.FirmwareSetting;
import com.willwinder.universalgcodesender.firmware.FirmwareSettingsException;
import com.willwinder.universalgcodesender.firmware.IFirmwareSettings;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetFirmwareVersionCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetBuildInfoCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.GetStatusCommand;
import com.willwinder.universalgcodesender.firmware.fluidnc.commands.SystemCommand;
import com.willwinder.universalgcodesender.listeners.ControllerState;
Expand All @@ -16,6 +16,8 @@
import com.willwinder.universalgcodesender.model.Position;
import com.willwinder.universalgcodesender.model.UnitUtils;
import com.willwinder.universalgcodesender.services.MessageService;
import static com.willwinder.universalgcodesender.utils.ControllerUtils.sendAndWaitForCompletion;
import static com.willwinder.universalgcodesender.utils.ControllerUtils.sendAndWaitForCompletionWithRetry;
import com.willwinder.universalgcodesender.utils.SemanticVersion;
import org.apache.commons.lang3.StringUtils;

Expand All @@ -24,9 +26,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.willwinder.universalgcodesender.utils.ControllerUtils.sendAndWaitForCompletion;
import static com.willwinder.universalgcodesender.utils.ControllerUtils.sendAndWaitForCompletionWithRetry;

public class FluidNCUtils {
public static final double GRBL_COMPABILITY_VERSION = 1.1d;
public static final SemanticVersion MINIMUM_VERSION = new SemanticVersion(3, 3, 0);
Expand Down Expand Up @@ -150,18 +149,18 @@ private static void addCapabilityIfSettingStartsWith(Capabilities capabilities,
* @throws Exception if the command couldn't be sent
* @throws IllegalStateException if the parsed version is not for a FluidNC controller or the version is too old
*/
public static GetFirmwareVersionCommand queryFirmwareVersion(IController controller, MessageService messageService) throws Exception {
public static GetBuildInfoCommand queryBuildInformation(IController controller, MessageService messageService) throws Exception {
messageService.dispatchMessage(MessageType.INFO, "*** Fetching device firmware version\n");
GetFirmwareVersionCommand getFirmwareVersionCommand = sendAndWaitForCompletion(controller, new GetFirmwareVersionCommand());
String firmwareVariant = getFirmwareVersionCommand.getFirmware();
SemanticVersion semanticVersion = getFirmwareVersionCommand.getVersion();
GetBuildInfoCommand getBuildInfoCommand = sendAndWaitForCompletion(controller, new GetBuildInfoCommand());
String firmwareVariant = getBuildInfoCommand.getFirmware();
SemanticVersion semanticVersion = getBuildInfoCommand.getVersion();

if (!firmwareVariant.equalsIgnoreCase("FluidNC") || semanticVersion.compareTo(MINIMUM_VERSION) < 0) {
messageService.dispatchMessage(MessageType.INFO, String.format("*** Expected a 'FluidNC %s' or later but got '%s %s'\n", MINIMUM_VERSION, firmwareVariant, semanticVersion));
throw new IllegalStateException("Unknown controller version: " + semanticVersion.toString());
}

return getFirmwareVersionCommand;
return getBuildInfoCommand;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ This file is part of Universal Gcode Sender (UGS).
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GetFirmwareVersionCommand extends SystemCommand {
public class GetBuildInfoCommand extends SystemCommand {
private static final Pattern VERSION_FLUIDNC_PATTERN = Pattern.compile("\\[VER:[0-9.]+ (?<variant>[a-zA-Z0-9]+) v?(?<version>(?<major>[0-9]*)(.(?<minor>[0-9]+)(.(?<patch>[0-9]+))?)?([a-zA-Z]+)?)(.*:.*)*]", Pattern.CASE_INSENSITIVE);
private static final Pattern VERSION_GRBL_PATTERN = Pattern.compile("\\[VER:(?<version>(?<major>[0-9]*)(.(?<minor>[0-9]+)(.(?<patch>[0-9]+))?)).*]", Pattern.CASE_INSENSITIVE);

public GetFirmwareVersionCommand() {
public GetBuildInfoCommand() {
super("$I");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright 2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS 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.
UGS 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 UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender.firmware.grbl;

/**
* GRBL options are reported by the build info command ($I). This enum contains the known options.
*/
public enum GrblBuildOption {
VARIABLE_SPINDLE_ENABLED("V"),
LINE_NUMBERS_ENABLED("N"),
MIST_COOLANT_ENABLED("M"),
CORE_XY_ENABLED("C"),
PARKING_MOTION_ENABLED("P"),
HOMING_FORCE_ORIGIN_ENABLED("Z"),
HOMING_SINGLE_AXIS_COMMAND_ENABLED("H"),
TWO_LIMIT_SWITCHES_ON_AXIS_ENABLED("T"),
TWO_ALLOW_OVERRIDE_ON_PROBING_ENABLED("A"),
USE_SPINDLE_DIRECTION_AS_ENABLE_PIN_ENABLED("D"),
SPINDLE_OFF_ON_ZERO_SPEED_ENABLED("0"),
SOFTWARE_LIMIT_PIN_DEBOUNCING_ENABLED("S"),
PARKING_OVERRIDE_CONTROL_ENABLED("R"),
SAFETY_DOOR_INPUT_ENABLED("+"),
RESTORE_ALL_EEPROM_DISABLED("*"),
RESTORE_EEPROM_SETTINGS_DISABLED("$"),
RESTORE_EEPROM_PARAMETER_DATA_DISABLED("#"),
BUILD_INFO_USER_STRING_DISABLED("I"),
FORCE_SYNC_ON_EEPROM_WRITE_DISABLED("E"),
FORCE_SYNC_ON_WORK_COORDINATE_CHANGE_DISABLED("W"),
HOMING_INITIALIZATION_LOCK_DISABLED("L"),
DUAL_AXIS_MOTORS_ENABLED("2");

private final String code;

GrblBuildOption(String code) {
this.code = code;
}

public String getCode() {
return code;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copyright 2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
UGS 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.
UGS 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 UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender.firmware.grbl;

import org.apache.commons.lang3.StringUtils;

/**
* Parses options from the build info command ($I)
* <a href="https://github.com/gnea/grbl/wiki/Grbl-v1.1-Interface#feedback-messages">Documentation</a>
*
* @author Joacim Breiler
*/
public class GrblBuildOptions {
private final String options;

public GrblBuildOptions() {
this("[OPT:]");
}

public GrblBuildOptions(String options) {
this.options = options;
}

public boolean isEnabled(GrblBuildOption grblBuildOption) {
String buildOptions = StringUtils.substringBefore(StringUtils.substringBetween(options, "[OPT:", "]"), ",");
return buildOptions.contains(grblBuildOption.getCode());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ This file is part of Universal Gcode Sender (UGS).
You should have received a copy of the GNU General Public License
along with UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender;
package com.willwinder.universalgcodesender.firmware.grbl;

/**
* Constants for defining additional capabilities from {@link CapabilitiesConstants} that a
* GRBL controller may support. The constants may be added to a {@link Capabilities} object.
* Constants for defining additional capabilities from {@link com.willwinder.universalgcodesender.CapabilitiesConstants} that a
* GRBL controller may support. The constants may be added to a {@link com.willwinder.universalgcodesender.Capabilities} object.
*
* @author Joacim Breiler
*/
Expand Down
Loading

0 comments on commit 19430be

Please sign in to comment.