
com.willwinder.universalgcodesender.firmware.grbl.GrblFirmwareSettings Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ugs-core Show documentation
Show all versions of ugs-core Show documentation
Universal Gcode Sender Library
The newest version!
/*
Copyright 2018 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 .
*/
package com.willwinder.universalgcodesender.firmware.grbl;
import com.willwinder.universalgcodesender.IController;
import com.willwinder.universalgcodesender.firmware.FirmwareSetting;
import com.willwinder.universalgcodesender.firmware.FirmwareSettingsException;
import com.willwinder.universalgcodesender.firmware.IFirmwareSettings;
import com.willwinder.universalgcodesender.firmware.IFirmwareSettingsListener;
import com.willwinder.universalgcodesender.i18n.Localization;
import com.willwinder.universalgcodesender.listeners.SerialCommunicatorListener;
import com.willwinder.universalgcodesender.model.Axis;
import com.willwinder.universalgcodesender.model.UnitUtils;
import com.willwinder.universalgcodesender.types.GcodeCommand;
import org.apache.commons.lang3.math.NumberUtils;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Handles the firmware settings on a GRBL controller. It needs to be registered as a listener
* to {@link com.willwinder.universalgcodesender.AbstractCommunicator#setListenAll(SerialCommunicatorListener)}
* for it to be able to process all commands to/from the controller.
*
* @author Joacim Breiler
* @author MerrellM
*/
public class GrblFirmwareSettings implements SerialCommunicatorListener, IFirmwareSettingsListener, IFirmwareSettings {
private static final Logger LOGGER = Logger.getLogger(GrblFirmwareSettings.class.getName());
/**
* Setting keys for GRBL
*/
private static final String KEY_REPORTING_UNITS_IN_INCHES = "$13";
private static final String KEY_SOFT_LIMITS_ENABLED = "$20";
private static final String KEY_HARD_LIMITS_ENABLED = "$21";
private static final String KEY_HOMING_ENABLED = "$22";
private static final String KEY_HOMING_INVERT_DIRECTION = "$23";
private static final String KEY_INVERT_DIRECTION = "$3";
private static final String KEY_INVERT_LIMIT_PINS = "$5";
private static final String KEY_STEPS_PER_MM_X = "$100";
private static final String KEY_STEPS_PER_MM_Y = "$101";
private static final String KEY_STEPS_PER_MM_Z = "$102";
private static final String KEY_SOFT_LIMIT_X = "$130";
private static final String KEY_SOFT_LIMIT_Y = "$131";
private static final String KEY_SOFT_LIMIT_Z = "$132";
/**
* A GRBL settings description lookups
*/
private final Map settings = new ConcurrentHashMap<>();
/**
* A delegate for all serial communication handling
*/
private final GrblFirmwareSettingsSerialCommunicator serialCommunicatorDelegate;
public GrblFirmwareSettings(IController controller) {
this.serialCommunicatorDelegate = new GrblFirmwareSettingsSerialCommunicator(controller);
this.serialCommunicatorDelegate.addListener(this);
}
/**
* Sets a property value on the controller. It will wait until the setting has been stored,
* if this fails a {@link FirmwareSettingsException} will be thrown.
*
* @param key the name of the setting to update
* @param value the value of the setting
* @return the value stored on the controller
* @throws FirmwareSettingsException if the value couldn't be persisted on the controller.
*/
@Override
synchronized public FirmwareSetting setValue(final String key, final String value) throws FirmwareSettingsException {
final FirmwareSetting oldSetting = getSetting(key)
.orElseThrow(() -> new FirmwareSettingsException("Couldn't find setting with key " + key + " to update."));
// The setting already contains the value so we do not update
if (oldSetting.getValue().equals(value)) {
return oldSetting;
}
// Make a copy of existing property and send it to our controller
final FirmwareSetting newSetting = new FirmwareSetting(oldSetting.getKey(), value, oldSetting.getUnits(), oldSetting.getDescription(), oldSetting.getShortDescription());
return serialCommunicatorDelegate
.updateSettingOnController(newSetting)
.orElse(oldSetting);
}
/**
* Sets a property value on the controller. It will wait until the setting has been stored,
* if this fails a {@link FirmwareSettingsException} will be thrown.
*
* @param key the name of the setting to update
* @param value the value of the setting
* @return the value stored on the controller
* @throws FirmwareSettingsException if the value couldn't be persisted on the controller.
*/
public FirmwareSetting setValue(final String key, final double value) throws FirmwareSettingsException {
final FirmwareSetting oldSetting = getSetting(key)
.orElseThrow(() -> new FirmwareSettingsException("Couldn't find setting with key " + key + " to update."));
// The setting already contains the value so we do not update
if (getValueAsDouble(key) == value) {
return oldSetting;
}
DecimalFormat decimalFormat = new DecimalFormat("0.0##", Localization.dfs);
return setValue(key, decimalFormat.format(value));
}
@Override
public void addListener(IFirmwareSettingsListener listener) {
serialCommunicatorDelegate.addListener(listener);
}
@Override
public void removeListener(IFirmwareSettingsListener listener) {
serialCommunicatorDelegate.removeListener(listener);
}
@Override
public Optional getSetting(String key) {
return Optional.ofNullable(settings.get(key));
}
@Override
public List getAllSettings() {
return new ArrayList<>(settings.values());
}
@Override
public boolean isHardLimitsEnabled() throws FirmwareSettingsException {
return getValueAsBoolean(KEY_HARD_LIMITS_ENABLED);
}
@Override
public void setHardLimitsEnabled(boolean enabled) throws FirmwareSettingsException {
setValue(KEY_HARD_LIMITS_ENABLED, enabled ? "1" : "0");
}
@Override
public boolean isSoftLimitsEnabled() throws FirmwareSettingsException {
return getValueAsBoolean(KEY_SOFT_LIMITS_ENABLED);
}
@Override
public void setSoftLimitsEnabled(boolean enabled) throws FirmwareSettingsException {
setValue(KEY_SOFT_LIMITS_ENABLED, enabled ? "1" : "0");
}
@Override
public boolean isInvertDirectionX() {
return (getInvertDirectionMask() & 1) == 1;
}
@Override
public void setInvertDirectionX(boolean inverted) throws FirmwareSettingsException {
Integer directionMask = getInvertDirectionMask();
if (inverted) {
directionMask |= 0b1; // set first bit from LSB
} else {
directionMask &= ~0b1; // unset first bit from LSB
}
setValue(KEY_INVERT_DIRECTION, String.valueOf(directionMask));
}
@Override
public boolean isInvertDirectionY() {
return (getInvertDirectionMask() & 2) == 2;
}
@Override
public void setInvertDirectionY(boolean inverted) throws FirmwareSettingsException {
Integer directionMask = getInvertDirectionMask();
if (inverted) {
directionMask |= 0b10; // set second bit from LSB
} else {
directionMask &= ~0b10; // unset second bit from LSB
}
setValue(KEY_INVERT_DIRECTION, String.valueOf(directionMask));
}
@Override
public boolean isInvertDirectionZ() {
return (getInvertDirectionMask() & 4) == 4;
}
@Override
public void setInvertDirectionZ(boolean inverted) throws FirmwareSettingsException {
int directionMask = getInvertDirectionMask();
if (inverted) {
directionMask |= 0b100; // set third bit from LSB
} else {
directionMask &= ~0b100; // unset third bit from LSB
}
setValue(KEY_INVERT_DIRECTION, String.valueOf(directionMask));
}
@Override
public int getStepsPerMillimeter(Axis axis) throws FirmwareSettingsException {
switch (axis) {
case X:
return getValueAsInteger(KEY_STEPS_PER_MM_X);
case Y:
return getValueAsInteger(KEY_STEPS_PER_MM_Y);
case Z:
return getValueAsInteger(KEY_STEPS_PER_MM_Z);
default:
return 0;
}
}
@Override
public double getSoftLimitX() throws FirmwareSettingsException {
return getValueAsDouble(KEY_SOFT_LIMIT_X);
}
@Override
public void setSoftLimitX(double limit) throws FirmwareSettingsException {
setValue(KEY_SOFT_LIMIT_X, limit);
}
@Override
public double getSoftLimitY() throws FirmwareSettingsException {
return getValueAsDouble(KEY_SOFT_LIMIT_Y);
}
@Override
public void setSoftLimitY(double limit) throws FirmwareSettingsException {
setValue(KEY_SOFT_LIMIT_Y, limit);
}
@Override
public double getSoftLimitZ() throws FirmwareSettingsException {
return getValueAsDouble(KEY_SOFT_LIMIT_Z);
}
@Override
public void setSoftLimitZ(double limit) throws FirmwareSettingsException {
setValue(KEY_SOFT_LIMIT_Z, limit);
}
@Override
public double getSoftLimit(Axis axis) throws FirmwareSettingsException {
switch (axis) {
case X:
return getSoftLimitX();
case Y:
return getSoftLimitY();
case Z:
return getSoftLimitZ();
default:
return 0;
}
}
@Override
public boolean isHomingDirectionInvertedX() {
return (getHomingInvertDirectionMask() & 1) == 1;
}
@Override
public void setHomingDirectionInvertedX(boolean inverted) throws FirmwareSettingsException {
Integer directionMask = getHomingInvertDirectionMask();
if (inverted) {
directionMask |= 0b1; // set first bit from LSB
} else {
directionMask &= ~0b1; // unset first bit from LSB
}
setValue(KEY_HOMING_INVERT_DIRECTION, String.valueOf(directionMask));
}
@Override
public boolean isHomingDirectionInvertedY() {
return (getHomingInvertDirectionMask() & 2) == 2;
}
@Override
public void setHomingDirectionInvertedY(boolean inverted) throws FirmwareSettingsException {
Integer directionMask = getHomingInvertDirectionMask();
if (inverted) {
directionMask |= 0b10; // set first bit from LSB
} else {
directionMask &= ~0b10; // unset first bit from LSB
}
setValue(KEY_HOMING_INVERT_DIRECTION, String.valueOf(directionMask));
}
@Override
public boolean isHomingDirectionInvertedZ() {
return (getHomingInvertDirectionMask() & 4) == 4;
}
@Override
public void setHomingDirectionInvertedZ(boolean inverted) throws FirmwareSettingsException {
Integer directionMask = getHomingInvertDirectionMask();
if (inverted) {
directionMask |= 0b100; // set first bit from LSB
} else {
directionMask &= ~0b100; // unset first bit from LSB
}
setValue(KEY_HOMING_INVERT_DIRECTION, String.valueOf(directionMask));
}
@Override
public void setHardLimitsInverted(boolean inverted) throws FirmwareSettingsException {
setValue(KEY_INVERT_LIMIT_PINS, inverted ? "1" : "0");
}
@Override
public boolean isHardLimitsInverted() throws FirmwareSettingsException {
return getValueAsBoolean(KEY_INVERT_LIMIT_PINS);
}
@Override
public void setSettings(List settings) throws FirmwareSettingsException {
settings.forEach(setting -> {
try {
setValue(setting.getKey(), setting.getValue());
} catch (FirmwareSettingsException e) {
LOGGER.warning("Couldn't set the firmware setting " + setting.getKey() + " to value " + setting.getValue() + ". Error message: " + e.getMessage());
}
});
}
private int getInvertDirectionMask() {
return getSetting(KEY_INVERT_DIRECTION)
.map(FirmwareSetting::getValue)
.map(Integer::valueOf)
.orElse(0);
}
private int getHomingInvertDirectionMask() {
return getSetting(KEY_HOMING_INVERT_DIRECTION)
.map(FirmwareSetting::getValue)
.map(Integer::valueOf)
.orElse(0);
}
@Override
public boolean isHomingEnabled() throws FirmwareSettingsException {
return getValueAsBoolean(KEY_HOMING_ENABLED);
}
@Override
public void setHomingEnabled(boolean enabled) throws FirmwareSettingsException {
setValue(KEY_HOMING_ENABLED, enabled ? "1" : "0");
}
@Override
public UnitUtils.Units getReportingUnits() {
return getSetting(KEY_REPORTING_UNITS_IN_INCHES)
.map(FirmwareSetting::getValue)
.map(value -> {
if ("0".equals(value)) {
return UnitUtils.Units.MM;
} else if ("1".equals(value)) {
return UnitUtils.Units.INCH;
} else {
return UnitUtils.Units.UNKNOWN;
}
})
.orElse(UnitUtils.Units.UNKNOWN);
}
/*
* SerialCommunicatorListener
*/
@Override
public void rawResponseListener(String response) {
serialCommunicatorDelegate.rawResponseListener(response);
}
@Override
public void commandSent(GcodeCommand command) {
serialCommunicatorDelegate.commandSent(command);
}
@Override
public void commandSkipped(GcodeCommand command) {
serialCommunicatorDelegate.commandSkipped(command);
}
@Override
public void communicatorPausedOnError() {
serialCommunicatorDelegate.communicatorPausedOnError();
}
/*
* IFirmwareSettingsListener
*/
@Override
public void onUpdatedFirmwareSetting(FirmwareSetting setting) {
LOGGER.log(Level.FINE, "Updating setting " + setting.getKey() + " = " + setting.getValue());
settings.put(setting.getKey(), setting);
}
/*
* Helpers
*/
private int getValueAsInteger(String key) throws FirmwareSettingsException {
FirmwareSetting firmwareSetting = getSetting(key).orElseThrow(() -> new FirmwareSettingsException("Couldn't find setting with key: " + key));
if (!NumberUtils.isNumber(firmwareSetting.getValue())) {
throw new FirmwareSettingsException("Expected the key " + key + " to contain a numeric value but was " + firmwareSetting.getValue());
}
return NumberUtils.createNumber(firmwareSetting.getValue()).intValue();
}
private double getValueAsDouble(String key) throws FirmwareSettingsException {
FirmwareSetting firmwareSetting = getSetting(key).orElseThrow(() -> new FirmwareSettingsException("Couldn't find setting with key: " + key));
if (!NumberUtils.isNumber(firmwareSetting.getValue())) {
throw new FirmwareSettingsException("Expected the key " + key + " to contain a numeric value but was " + firmwareSetting.getValue());
}
return NumberUtils.createNumber(firmwareSetting.getValue()).doubleValue();
}
private boolean getValueAsBoolean(String key) throws FirmwareSettingsException {
FirmwareSetting firmwareSetting = getSetting(key).orElseThrow(() -> new FirmwareSettingsException("Couldn't find setting with key: " + key));
return "1".equalsIgnoreCase(firmwareSetting.getValue());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy