Skip to content

Commit

Permalink
Reworked the overrides panel (#2446)
Browse files Browse the repository at this point in the history
* Refactored overrides and accessory states as records and created builders.
* Added an override manger that will send feed override commands continuously to match target feed and spindle speeds. Replaced override buttons with sliders.
* Added analog actions that can be mapped to gamepads for controlling the feed and spindle speed overrides.
  • Loading branch information
breiler committed Jan 31, 2024
1 parent a964441 commit 9ff4267
Show file tree
Hide file tree
Showing 45 changed files with 2,022 additions and 628 deletions.
Expand Up @@ -20,14 +20,15 @@ This file is part of Universal Gcode Sender (UGS).

import com.google.gson.JsonObject;
import com.willwinder.universalgcodesender.communicator.ICommunicator;
import com.willwinder.universalgcodesender.firmware.IOverrideManager;
import com.willwinder.universalgcodesender.firmware.g2core.G2CoreOverrideManager;
import com.willwinder.universalgcodesender.listeners.ControllerState;
import com.willwinder.universalgcodesender.listeners.ControllerStatus;
import com.willwinder.universalgcodesender.listeners.ControllerStatusBuilder;
import com.willwinder.universalgcodesender.listeners.MessageType;
import com.willwinder.universalgcodesender.model.CommunicatorState;
import com.willwinder.universalgcodesender.model.PartialPosition;

import static com.willwinder.universalgcodesender.model.CommunicatorState.COMM_IDLE;
import com.willwinder.universalgcodesender.model.PartialPosition;

/**
* G2Core Control layer.
Expand All @@ -41,13 +42,16 @@ public class G2CoreController extends TinyGController {
* A temporary flag for emulating a JOG state when parsing the controller status
*/
private boolean isJogging = false;
private final IOverrideManager overrideManager;

public G2CoreController() {
super();
overrideManager = new G2CoreOverrideManager(this, getCommunicator());
}

public G2CoreController(ICommunicator communicator) {
super(communicator);
overrideManager = new G2CoreOverrideManager(this, communicator);
}

@Override
Expand Down Expand Up @@ -90,7 +94,7 @@ protected void handleReadyResponse(String response, JsonObject jo) {
capabilities.addCapability(CapabilitiesConstants.CONTINUOUS_JOGGING);
capabilities.addCapability(CapabilitiesConstants.HOMING);
capabilities.addCapability(CapabilitiesConstants.FIRMWARE_SETTINGS);
capabilities.addCapability(CapabilitiesConstants.OVERRIDES);
capabilities.removeCapability(CapabilitiesConstants.OVERRIDES);
capabilities.removeCapability(CapabilitiesConstants.SETUP_WIZARD);

setCurrentState(COMM_IDLE);
Expand Down Expand Up @@ -191,4 +195,9 @@ protected ControllerStatus parseControllerStatus(JsonObject jo) {

return controllerStatus;
}

@Override
public IOverrideManager getOverrideManager() {
return overrideManager;
}
}
@@ -1,5 +1,5 @@
/*
Copyright 2013-2023 Will Winder
Copyright 2013-2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
Expand Down Expand Up @@ -33,7 +33,8 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.model.Alarm;
import com.willwinder.universalgcodesender.model.Axis;
import com.willwinder.universalgcodesender.model.CommunicatorState;
import com.willwinder.universalgcodesender.model.Overrides;
import static com.willwinder.universalgcodesender.model.CommunicatorState.COMM_CHECK;
import static com.willwinder.universalgcodesender.model.CommunicatorState.COMM_IDLE;
import com.willwinder.universalgcodesender.model.PartialPosition;
import com.willwinder.universalgcodesender.model.Position;
import com.willwinder.universalgcodesender.model.UnitUtils.Units;
Expand All @@ -42,16 +43,15 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.types.GrblSettingMessage;
import com.willwinder.universalgcodesender.utils.ControllerUtils;
import com.willwinder.universalgcodesender.utils.GrblLookups;
import com.willwinder.universalgcodesender.firmware.grbl.GrblOverrideManager;
import com.willwinder.universalgcodesender.firmware.IOverrideManager;
import com.willwinder.universalgcodesender.utils.ThreadHelper;
import org.apache.commons.lang3.StringUtils;

import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.willwinder.universalgcodesender.model.CommunicatorState.COMM_CHECK;
import static com.willwinder.universalgcodesender.model.CommunicatorState.COMM_IDLE;

/**
* GRBL Control layer, coordinates all aspects of control.
*
Expand All @@ -63,10 +63,16 @@ public class GrblController extends AbstractController {
private static final GrblLookups ERRORS = new GrblLookups("error_codes");
private final StatusPollTimer positionPollTimer;
private final GrblFirmwareSettings firmwareSettings;
private final IOverrideManager overrideManager;
private GrblControllerInitializer initializer;
private Capabilities capabilities = new Capabilities();
// Polling state
private ControllerStatus controllerStatus = new ControllerStatus(ControllerState.DISCONNECTED, new Position(0, 0, 0, Units.MM), new Position(0, 0, 0, Units.MM));
private ControllerStatus controllerStatus = ControllerStatusBuilder.newInstance()
.setState(ControllerState.DISCONNECTED)
.setWorkCoord(Position.ZERO)
.setMachineCoord(Position.ZERO)
.build();

// Canceling state
private Boolean isCanceling = false; // Set for the position polling thread.
private int attemptsRemaining;
Expand All @@ -88,6 +94,7 @@ public GrblController(ICommunicator communicator) {
this.firmwareSettings = new GrblFirmwareSettings(this);
this.comm.addListener(firmwareSettings);
this.initializer = new GrblControllerInitializer(this);
this.overrideManager = new GrblOverrideManager(this, communicator);
}

public GrblController() {
Expand Down Expand Up @@ -556,6 +563,11 @@ public ControllerStatus getControllerStatus() {
return controllerStatus;
}

@Override
public IOverrideManager getOverrideManager() {
return overrideManager;
}

// No longer a listener event
private void handleStatusString(final String string) {
if (this.capabilities == null) {
Expand Down Expand Up @@ -641,15 +653,6 @@ protected void setControllerState(ControllerState controllerState) {
dispatchStatusString(controllerStatus);
}

@Override
public void sendOverrideCommand(Overrides command) throws Exception {
Byte realTimeCommand = GrblUtils.getOverrideForEnum(command, capabilities);
if (realTimeCommand != null) {
this.dispatchConsoleMessage(MessageType.INFO, String.format(">>> 0x%02x\n", realTimeCommand));
this.comm.sendByteImmediately(realTimeCommand);
}
}

@Override
public boolean getStatusUpdatesEnabled() {
return positionPollTimer.isEnabled();
Expand Down
86 changes: 65 additions & 21 deletions ugs-core/src/com/willwinder/universalgcodesender/GrblUtils.java
@@ -1,5 +1,5 @@
/*
Copyright 2012-2023 Will Winder
Copyright 2012-2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
Expand All @@ -21,22 +21,29 @@ This file is part of Universal Gcode Sender (UGS).

import com.willwinder.universalgcodesender.firmware.grbl.commands.GetStatusCommand;
import com.willwinder.universalgcodesender.firmware.grbl.commands.GrblSystemCommand;
import com.willwinder.universalgcodesender.listeners.AccessoryStates;
import com.willwinder.universalgcodesender.listeners.AccessoryStatesBuilder;
import com.willwinder.universalgcodesender.listeners.ControllerState;
import com.willwinder.universalgcodesender.listeners.ControllerStatus;
import com.willwinder.universalgcodesender.listeners.ControllerStatus.AccessoryStates;
import com.willwinder.universalgcodesender.listeners.ControllerStatus.EnabledPins;
import com.willwinder.universalgcodesender.listeners.ControllerStatus.OverridePercents;
import com.willwinder.universalgcodesender.listeners.ControllerStatusBuilder;
import com.willwinder.universalgcodesender.listeners.EnabledPins;
import com.willwinder.universalgcodesender.listeners.EnabledPinsBuilder;
import com.willwinder.universalgcodesender.listeners.MessageType;
import com.willwinder.universalgcodesender.model.*;
import com.willwinder.universalgcodesender.listeners.OverridePercents;
import com.willwinder.universalgcodesender.model.Alarm;
import com.willwinder.universalgcodesender.model.Axis;
import com.willwinder.universalgcodesender.model.Overrides;
import com.willwinder.universalgcodesender.model.PartialPosition;
import com.willwinder.universalgcodesender.model.Position;
import com.willwinder.universalgcodesender.model.UnitUtils.Units;
import static com.willwinder.universalgcodesender.utils.ControllerUtils.sendAndWaitForCompletion;
import static com.willwinder.universalgcodesender.utils.ControllerUtils.sendAndWaitForCompletionWithRetry;
import org.apache.commons.lang3.StringUtils;

import java.util.Optional;
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;

/**
* Collection of useful Grbl related utilities.
*
Expand Down Expand Up @@ -347,10 +354,11 @@ static protected ControllerStatus getStatusFromStatusString(
public static ControllerStatus getStatusFromStatusStringLegacy(String status, Units reportingUnits) {
String stateString = StringUtils.defaultString(getStateFromStatusString(status), "unknown");
ControllerState state = getControllerStateFromStateString(stateString);
return new ControllerStatus(
state,
getMachinePositionFromStatusString(status, reportingUnits),
getWorkPositionFromStatusString(status, reportingUnits));
return ControllerStatusBuilder.newInstance()
.setState(state)
.setWorkCoord(getWorkPositionFromStatusString(status, reportingUnits))
.setMachineCoord(getMachinePositionFromStatusString(status, reportingUnits))
.build();
}

/**
Expand Down Expand Up @@ -404,13 +412,7 @@ else if (part.startsWith("WCO:")) {
}
else if (part.startsWith("Ov:")) {
isOverrideReport = true;
String[] overrideParts = part.substring(3).trim().split(",");
if (overrideParts.length == 3) {
overrides = new OverridePercents(
Integer.parseInt(overrideParts[0]),
Integer.parseInt(overrideParts[1]),
Integer.parseInt(overrideParts[2]));
}
overrides = parseOverrides(part).orElse(OverridePercents.EMTPY_OVERRIDE_PERCENTS);
}
else if (part.startsWith("F:")) {
feedSpeed = parseFeedSpeed(part);
Expand All @@ -422,11 +424,11 @@ else if (part.startsWith("FS:")) {
}
else if (part.startsWith("Pn:")) {
String value = part.substring(part.indexOf(':')+1);
pins = new EnabledPins(value);
pins = parseEnabledPins(value);
}
else if (part.startsWith("A:")) {
String value = part.substring(part.indexOf(':')+1);
accessoryStates = new AccessoryStates(value);
accessoryStates = parseAccessoryStates(value);
}
}

Expand Down Expand Up @@ -461,6 +463,48 @@ else if (part.startsWith("A:")) {
return new ControllerStatus(state, subStateString, MPos, WPos, feedSpeed, reportingUnits, spindleSpeed, overrides, WCO, pins, accessoryStates);
}

private static Optional<OverridePercents> parseOverrides(String value) {
String[] overrideParts = value.substring(3).trim().split(",");
if (overrideParts.length == 3) {
return Optional.of(new OverridePercents(
Integer.parseInt(overrideParts[0]),
Integer.parseInt(overrideParts[1]),
Integer.parseInt(overrideParts[2])));
}
return Optional.empty();
}

private static EnabledPins parseEnabledPins(String value) {
String enabledUpper = value.toUpperCase();
return new EnabledPinsBuilder()
.setX(enabledUpper.contains("X"))
.setY(enabledUpper.contains("Y"))
.setZ(enabledUpper.contains("Z"))
.setA(enabledUpper.contains("A"))
.setB(enabledUpper.contains("B"))
.setC(enabledUpper.contains("C"))
.setProbe(enabledUpper.contains("P"))
.setDoor(enabledUpper.contains("D"))
.setHold(enabledUpper.contains("H"))
.setSoftReset(enabledUpper.contains("R"))
.setCycleStart(enabledUpper.contains("S"))
.createEnabledPins();
}

/**
* Parses the accessory state string
*
* @param accessoryStates as a string
* @return the parsed accessory state
*/
private static AccessoryStates parseAccessoryStates(String accessoryStates) {
String enabledUpper = accessoryStates.toUpperCase();
boolean spindleCW = enabledUpper.contains("S");
boolean flood = enabledUpper.contains("F");
boolean mist = enabledUpper.contains("M");
return new AccessoryStatesBuilder().setSpindleCW(spindleCW).setFlood(flood).setMist(mist).createAccessoryStates();
}

/**
* Parses the feed speed from a status string starting with "F:".
* The supported formats are F:1000.0 or F:3000.0,100.0,100.0 which are current feed rate, requested feed rate and override feed rate
Expand Down
18 changes: 10 additions & 8 deletions ugs-core/src/com/willwinder/universalgcodesender/IController.java
@@ -1,5 +1,5 @@
/*
Copyright 2015-2023 Will Winder
Copyright 2015-2024 Will Winder
This file is part of Universal Gcode Sender (UGS).
Expand All @@ -26,13 +26,13 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.listeners.ControllerListener;
import com.willwinder.universalgcodesender.listeners.ControllerStatus;
import com.willwinder.universalgcodesender.model.Axis;
import com.willwinder.universalgcodesender.model.Overrides;
import com.willwinder.universalgcodesender.model.PartialPosition;
import com.willwinder.universalgcodesender.model.CommunicatorState;
import com.willwinder.universalgcodesender.model.PartialPosition;
import com.willwinder.universalgcodesender.model.UnitUtils;
import com.willwinder.universalgcodesender.services.MessageService;
import com.willwinder.universalgcodesender.types.GcodeCommand;
import com.willwinder.universalgcodesender.utils.IGcodeStreamReader;
import com.willwinder.universalgcodesender.firmware.IOverrideManager;

import java.util.Optional;

Expand Down Expand Up @@ -129,11 +129,6 @@ public interface IController {
void probe(String axis, double feedRate, double distance, UnitUtils.Units units) throws Exception;
void offsetTool(String axis, double offset, UnitUtils.Units units) throws Exception;

/*
Overrides
*/
void sendOverrideCommand(Overrides command) throws Exception;

/*
Behavior
*/
Expand Down Expand Up @@ -246,4 +241,11 @@ public interface IController {
* @return a command creator for this controller
*/
ICommandCreator getCommandCreator();

/**
* Gets the manager for handling overrides.
*
* @return the override manager.
*/
IOverrideManager getOverrideManager();
}

0 comments on commit 9ff4267

Please sign in to comment.