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.
Original file line number Diff line number Diff line change
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;
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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();
}
Loading

0 comments on commit 9ff4267

Please sign in to comment.