Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for gcode states and work coordinates #1078

Merged
merged 14 commits into from
Jul 16, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,6 @@ protected Boolean isIdleEvent() {
return getControlState() == COMM_IDLE || getControlState() == COMM_CHECK;
}

@Override
public void restoreParserModalState() {
// no op
}

@Override
protected void rawResponseHandler(String response) {
JsonObject jo;
Expand Down Expand Up @@ -216,8 +211,10 @@ private void updateControllerStatus(JsonObject jo) {
ControllerState previousState = controllerStatus.getState();
UGSEvent.ControlState previousControlState = getControlState(previousState);

TinyGUtils.updateGcodeState(this, jo);

// Notify our listeners about the new status
controllerStatus = TinyGUtils.updateControllerStatus(controllerStatus, jo);
controllerStatus = TinyGUtils.updateControllerStatus(controllerStatus, getCurrentGcodeState(), jo);
dispatchStatusString(controllerStatus);

// Notify state change to our listeners
Expand Down Expand Up @@ -245,6 +242,9 @@ private void sendInitCommands() {
// 0=off, 1=filtered, 2=verbose
comm.queueStringForComm("{sv:1}");

// Configure status reports
comm.queueStringForComm("{sr:{posx:t, posy:t, posz:t, mpox:t, mpoy:t, mpoz:t, plan:t, vel:t, unit:t, stat:t, dist:t, admo:t, frmo:t, coor:t}}");

// Request initial status report
comm.queueStringForComm("{sr:n}");

Expand All @@ -256,12 +256,13 @@ private void sendInitCommands() {

@Override
public void performHomingCycle() throws Exception {
sendCommandImmediately(new GcodeCommand("G28.2 Z0 X0 Y0"));
sendCommandImmediately(new GcodeCommand("G28.2 X0 Y0 Z0"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a data point, this won't make any functional difference. The Z axis will be homed first anyway, as g2core regards that as being safer than homing it after the others.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this is from a bad merge. It's better having the Z probe first as it will reflect the correct order in g2core, will fix this soon!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries at all. 😄

}

@Override
public void resetCoordinatesToZero() throws Exception {
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
String command = TinyGUtils.generateResetCoordinatesToZeroCommand(controllerStatus, getCurrentGcodeState());
sendCommandImmediately(new GcodeCommand(command));
}

@Override
Expand Down Expand Up @@ -292,11 +293,7 @@ public void viewParserState() throws Exception {

@Override
public void updateParserModalState(GcodeCommand command) {
if (command.getCommandString().startsWith("{\"gc")) {
String gcode = StringUtils.substringBetween(command.getCommandString(), "{\"gc\":\"", "\"");
GcodeCommand gcodeCommand = new GcodeCommand(gcode);
super.updateParserModalState(gcodeCommand);
}
// This is handled by the rawResponseHandler
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calls parser.addCommand which updates the on-the-fly GcodeParser, that may need to be called somewhere. I think my plan was to deprecate distanceModeCode / unitsCode in favor of the real gcode parser.

}

@Override
Expand All @@ -312,7 +309,8 @@ public void softReset() throws Exception {

@Override
public void setWorkPosition(Axis axis, double position) throws Exception {
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
String command = TinyGUtils.generateSetWorkPositionCommand(controllerStatus, getCurrentGcodeState(), axis, position);
sendCommandImmediately(new GcodeCommand(command));
}

@Override
Expand Down
206 changes: 188 additions & 18 deletions ugs-core/src/com/willwinder/universalgcodesender/TinyGUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ This file is part of Universal Gcode Sender (UGS).

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.willwinder.universalgcodesender.gcode.GcodeState;
import com.willwinder.universalgcodesender.gcode.util.Code;
import com.willwinder.universalgcodesender.gcode.util.Plane;
import com.willwinder.universalgcodesender.listeners.ControllerState;
import com.willwinder.universalgcodesender.listeners.ControllerStatus;
import com.willwinder.universalgcodesender.model.Axis;
import com.willwinder.universalgcodesender.model.Position;
import com.willwinder.universalgcodesender.model.UnitUtils;
import org.apache.commons.lang3.StringUtils;

import static com.willwinder.universalgcodesender.gcode.util.Code.G20;

/**
* Common utils for TinyG controllers
*
Expand All @@ -45,16 +51,24 @@ public class TinyGUtils {
public static final String COMMAND_STATUS_REPORT = "{sr:n}";
public static final String COMMAND_KILL_ALARM_LOCK = "{clear:n}";

public static final String FIELD_FIRMWARE_VERSION = "fv";
public static final String FIELD_RESPONSE = "r";
private static final String FIELD_FIRMWARE_VERSION = "fv";
private static final String FIELD_RESPONSE = "r";

public static final String FIELD_STATUS_RESULT = "sr";
public static final String FIELD_STATUS_RESULT_UNIT = "unit";
public static final String FIELD_STATUS_RESULT_POSX = "posx";
public static final String FIELD_STATUS_REPORT_POSY = "posy";
public static final String FIELD_STATUS_REPORT_POSZ = "posz";
public static final String FIELD_STATUS_REPORT_VELOCITY = "vel";
public static final String FIELD_STATUS_REPORT_STATUS = "stat";
private static final String FIELD_STATUS_RESULT_UNIT = "unit";
private static final String FIELD_STATUS_RESULT_POSX = "posx";
private static final String FIELD_STATUS_REPORT_POSY = "posy";
private static final String FIELD_STATUS_REPORT_POSZ = "posz";
private static final String FIELD_STATUS_REPORT_VELOCITY = "vel";
private static final String FIELD_STATUS_REPORT_COORD = "coor";
private static final String FIELD_STATUS_REPORT_PLANE = "plan";
private static final String FIELD_STATUS_REPORT_DISTANCE_MODE = "dist";
private static final String FIELD_STATUS_REPORT_ARC_DISTANCE_MODE = "admo";
private static final String FIELD_STATUS_REPORT_FEED_MODE = "frmo";
private static final String FIELD_STATUS_REPORT_STATUS = "stat";
private static final String FIELD_STATUS_REPORT_MPOX = "mpox";
private static final String FIELD_STATUS_REPORT_MPOY = "mpoy";
private static final String FIELD_STATUS_REPORT_MPOZ = "mpoz";

private static JsonParser parser = new JsonParser();

Expand Down Expand Up @@ -98,7 +112,7 @@ public static boolean isReadyResponse(JsonObject response) {
JsonObject jo = response.getAsJsonObject(FIELD_RESPONSE);
if (jo.has("msg")) {
String msg = jo.get("msg").getAsString();
return StringUtils.equals(msg,"SYSTEM READY");
return StringUtils.equals(msg, "SYSTEM READY");
}
}
return false;
Expand All @@ -115,26 +129,36 @@ public static boolean isStatusResponse(JsonObject response) {
* @param response the response string from the controller
* @return a new updated controller status
*/
public static ControllerStatus updateControllerStatus(final ControllerStatus lastControllerStatus, final JsonObject response) {
public static ControllerStatus updateControllerStatus(final ControllerStatus lastControllerStatus, final GcodeState lastGcodeState, final JsonObject response) {
if (response.has(FIELD_STATUS_RESULT)) {
JsonObject statusResultObject = response.getAsJsonObject(FIELD_STATUS_RESULT);

Position machineCoord = lastControllerStatus.getMachineCoord();
UnitUtils.Units currentUnits = lastGcodeState.units == G20 ? UnitUtils.Units.INCH : UnitUtils.Units.MM;

Position workCoord = lastControllerStatus.getWorkCoord().getPositionIn(currentUnits);
if (statusResultObject.has(FIELD_STATUS_RESULT_POSX)) {
machineCoord.setX(statusResultObject.get(FIELD_STATUS_RESULT_POSX).getAsDouble());
workCoord.setX(statusResultObject.get(FIELD_STATUS_RESULT_POSX).getAsDouble());
}

if (statusResultObject.has(FIELD_STATUS_REPORT_POSY)) {
machineCoord.setY(statusResultObject.get(FIELD_STATUS_REPORT_POSY).getAsDouble());
workCoord.setY(statusResultObject.get(FIELD_STATUS_REPORT_POSY).getAsDouble());
}

if (statusResultObject.has(FIELD_STATUS_REPORT_POSZ)) {
machineCoord.setZ(statusResultObject.get(FIELD_STATUS_REPORT_POSZ).getAsDouble());
workCoord.setZ(statusResultObject.get(FIELD_STATUS_REPORT_POSZ).getAsDouble());
}

Position machineCoord = lastControllerStatus.getMachineCoord().getPositionIn(UnitUtils.Units.MM);
if (statusResultObject.has(FIELD_STATUS_REPORT_MPOX)) {
machineCoord.setX(statusResultObject.get(FIELD_STATUS_REPORT_MPOX).getAsDouble());
}

if (statusResultObject.has(FIELD_STATUS_REPORT_MPOY)) {
machineCoord.setY(statusResultObject.get(FIELD_STATUS_REPORT_MPOY).getAsDouble());
}

if (statusResultObject.has(FIELD_STATUS_RESULT_UNIT)) {
UnitUtils.Units units = statusResultObject.get(FIELD_STATUS_RESULT_UNIT).getAsInt() == 0 ? UnitUtils.Units.INCH : UnitUtils.Units.MM;
machineCoord = new Position(machineCoord.getX(), machineCoord.getY(), machineCoord.getZ(), units);
if (statusResultObject.has(FIELD_STATUS_REPORT_MPOZ)) {
machineCoord.setZ(statusResultObject.get(FIELD_STATUS_REPORT_MPOZ).getAsDouble());
}

Double feedSpeed = lastControllerStatus.getFeedSpeed();
Expand All @@ -155,7 +179,7 @@ public static ControllerStatus updateControllerStatus(final ControllerStatus las
ControllerStatus.EnabledPins enabledPins = lastControllerStatus.getEnabledPins();
ControllerStatus.AccessoryStates accessoryStates = lastControllerStatus.getAccessoryStates();

return new ControllerStatus(stateString, state, machineCoord, machineCoord, feedSpeed, spindleSpeed, overrides, workCoordinateOffset, enabledPins, accessoryStates);
return new ControllerStatus(stateString, state, machineCoord.getPositionIn(currentUnits), workCoord.getPositionIn(currentUnits), feedSpeed, spindleSpeed, overrides, workCoordinateOffset, enabledPins, accessoryStates);
}

return lastControllerStatus;
Expand Down Expand Up @@ -206,4 +230,150 @@ private static String getStateAsString(int state) {
ControllerState controllerState = getState(state);
return controllerState.name();
}

/**
* Generates a command for resetting the coordinates for the current coordinate system to zero.
*
* @param controllerStatus the current controller status
* @param gcodeState the current gcode state
* @return a string with the command to reset the coordinate system to zero
*/
public static String generateResetCoordinatesToZeroCommand(ControllerStatus controllerStatus, GcodeState gcodeState) {
int offsetCode = convertOffsetGcodeToCode(gcodeState.offset);
Position machineCoord = controllerStatus.getMachineCoord();
return "G10 L2 P" + offsetCode +
" X" + Utils.formatter.format(machineCoord.get(Axis.X)) +
" Y" + Utils.formatter.format(machineCoord.get(Axis.Y)) +
" Z" + Utils.formatter.format(machineCoord.get(Axis.Z));
}

/**
* Generates a command for setting the axis to a position in the current coordinate system
*
* @param controllerStatus the current controller status
* @param gcodeState the current gcode state
* @param axis the axis to set
* @param position the position to set
* @return a command for setting the position
*/
public static String generateSetWorkPositionCommand(ControllerStatus controllerStatus, GcodeState gcodeState, Axis axis, double position) {
int offsetCode = convertOffsetGcodeToCode(gcodeState.offset);
Position machineCoord = controllerStatus.getMachineCoord();
double coordinate = -(position - machineCoord.get(axis));
return "G10 L2 P" + offsetCode + " " +
axis.name() + Utils.formatter.format(coordinate);
}

private static int convertOffsetGcodeToCode(Code offsetGcode) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could go right into GcodeUtils.java, maybe even some of the other WCS helpers you have here as well. There is also WorkCoordinateSystem.java, so something like this might technically work:

 WorkCoordinateSystem.valueOf(offsetGcode.toString()).getPValue();

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably lean towards keeping your static method and maybe getting rid of WorkCoordinateSystem

switch (offsetGcode) {
case G54:
return 1;
case G55:
return 2;
case G56:
return 3;
case G57:
return 4;
case G58:
return 5;
case G59:
return 6;
default:
return 0;
}
}

private static Code convertOffsetCodeToGcode(int offsetCode) {
switch (offsetCode) {
case 1:
return Code.G54;
case 2:
return Code.G55;
case 3:
return Code.G56;
case 4:
return Code.G57;
case 5:
return Code.G58;
case 6:
return Code.G59;
default:
return Code.G53;
}
}

/**
* Updates the Gcode state from the response if it contains a status line
*
* @param controller the controller to update
* @param response the response to parse the gcode state from
*/
public static void updateGcodeState(TinyGController controller, JsonObject response) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big fan of the side effects in here, is there any reason why this can't be merged with the code updating the ControllerStatus? You could check with getCurrentParserState to get the state.

The GRBL version could be updated to use getCurrentParserState instead of GrblFeedbackMessage. The second line here literally sends the grbl $G output to the gcode parser to make sure it is up to date:

            else if (GrblUtils.isGrblFeedbackMessage(response, capabilities)) {
                GrblFeedbackMessage grblFeedbackMessage = new GrblFeedbackMessage(response);
                // Convert feedback message to raw commands to update modal state.
                this.updateParserModalState(new GcodeCommand(GrblUtils.parseFeedbackMessage(response, capabilities)));
                this.verboseMessageForConsole(grblFeedbackMessage.toString() + "\n");
                setDistanceModeCode(grblFeedbackMessage.getDistanceMode());
                setUnitsCode(grblFeedbackMessage.getUnits());
                dispatchStateChange(COMM_IDLE);
            }

if (response.has(TinyGUtils.FIELD_STATUS_RESULT)) {
GcodeState gcodeState = controller.getCurrentGcodeState();
JsonObject statusResultObject = response.getAsJsonObject(TinyGUtils.FIELD_STATUS_RESULT);

if (statusResultObject.has(TinyGUtils.FIELD_STATUS_REPORT_COORD)) {
int offsetCode = statusResultObject.get(TinyGUtils.FIELD_STATUS_REPORT_COORD).getAsInt();
gcodeState.offset = TinyGUtils.convertOffsetCodeToGcode(offsetCode);
}

if (statusResultObject.has(TinyGUtils.FIELD_STATUS_RESULT_UNIT)) {
int units = statusResultObject.get(TinyGUtils.FIELD_STATUS_RESULT_UNIT).getAsInt();
// 0=inch, 1=mm
if (units == 0) {
gcodeState.units = Code.G20;
controller.setUnitsCode(Code.G20.name());
} else {
gcodeState.units = Code.G21;
controller.setUnitsCode(Code.G21.name());
}
}

if (statusResultObject.has(TinyGUtils.FIELD_STATUS_REPORT_PLANE)) {
int plane = statusResultObject.get(TinyGUtils.FIELD_STATUS_REPORT_PLANE).getAsInt();
// 0=XY plane, 1=XZ plane, 2=YZ plane
if (plane == 0) {
gcodeState.plane = Plane.XY;
} else if (plane == 1) {
gcodeState.plane = Plane.ZX;
} else if (plane == 2) {
gcodeState.plane = Plane.YZ;
}
}

if (statusResultObject.has(TinyGUtils.FIELD_STATUS_REPORT_FEED_MODE)) {
int feedMode = statusResultObject.get(TinyGUtils.FIELD_STATUS_REPORT_FEED_MODE).getAsInt();
// 0=units-per-minute-mode, 1=inverse-time-mode
if (feedMode == 0) {
gcodeState.feedMode = Code.G93;
} else if (feedMode == 1) {
gcodeState.feedMode = Code.G94;
}
}

if (statusResultObject.has(TinyGUtils.FIELD_STATUS_REPORT_DISTANCE_MODE)) {
int distance = statusResultObject.get(TinyGUtils.FIELD_STATUS_REPORT_DISTANCE_MODE).getAsInt();
// 0=absolute distance mode, 1=incremental distance mode
if (distance == 0) {
gcodeState.distanceMode = Code.G90;
controller.setDistanceModeCode(Code.G90.name());
} else if (distance == 1) {
gcodeState.distanceMode = Code.G91;
controller.setDistanceModeCode(Code.G91.name());
}
}

if (statusResultObject.has(TinyGUtils.FIELD_STATUS_REPORT_ARC_DISTANCE_MODE)) {
int arcDistance = statusResultObject.get(TinyGUtils.FIELD_STATUS_REPORT_ARC_DISTANCE_MODE).getAsInt();
// 0=absolute distance mode, 1=incremental distance mode
if (arcDistance == 0) {
gcodeState.arcDistanceMode = Code.G90_1;
} else if (arcDistance == 1) {
gcodeState.arcDistanceMode = Code.G91_1;
}
}
}

}
}
Loading