Skip to content

Commit

Permalink
G-code and menu item
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkyhead committed Oct 4, 2024
1 parent 576986a commit a934f40
Show file tree
Hide file tree
Showing 25 changed files with 221 additions and 26 deletions.
3 changes: 3 additions & 0 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,9 @@
// Homing speeds (linear=mm/min, rotational=°/min)
#define HOMING_FEEDRATE_MM_M { (50*60), (50*60), (4*60) }

// Edit homing feedrates with M210 and MarlinUI menu items
//#define EDITABLE_HOMING_FEEDRATE

// Validate that endstops are triggered on homing moves
#define VALIDATE_HOMING_ENDSTOPS

Expand Down
32 changes: 21 additions & 11 deletions Marlin/src/core/language.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@
#define STR_MAX_ACCELERATION "Max Acceleration (units/s2)"
#define STR_MAX_FEEDRATES "Max feedrates (units/s)"
#define STR_ACCELERATION_P_R_T "Acceleration (units/s2) (P<print-accel> R<retract-accel> T<travel-accel>)"
#define STR_HOMING_FEEDRATE "Homing Feedrate"
#define STR_TOOL_CHANGING "Tool-changing"
#define STR_HOTEND_OFFSETS "Hotend offsets"
#define STR_SERVO_ANGLES "Servo Angles"
Expand Down Expand Up @@ -358,24 +359,33 @@
#define STR_CALIBRATION "calibration"

// General axis names
#define STR_X "X"
#define STR_Y "Y"
#define STR_Z "Z"
#if HAS_X_AXIS
#define AXIS1_NAME 'X'
#define STR_X "X"
#endif
#if HAS_Y_AXIS
#define AXIS2_NAME 'Y'
#define STR_Y "Y"
#endif
#if HAS_Z_AXIS
#define AXIS3_NAME 'Z'
#define STR_Z "Z"
#endif
#define STR_E "E"
#if IS_KINEMATIC
#define STR_A "A"
#define STR_B "B"
#define STR_C "C"
#else
#define STR_A "X"
#define STR_B "Y"
#define STR_C "Z"
#define STR_A STR_X
#define STR_B STR_Y
#define STR_C STR_Z
#endif
#define STR_X2 "X2"
#define STR_Y2 "Y2"
#define STR_Z2 "Z2"
#define STR_Z3 "Z3"
#define STR_Z4 "Z4"
#define STR_X2 STR_A "2"
#define STR_Y2 STR_B "2"
#define STR_Z2 STR_C "2"
#define STR_Z3 STR_C "3"
#define STR_Z4 STR_C "4"

// Extra Axis and Endstop Names
#if HAS_I_AXIS
Expand Down
2 changes: 2 additions & 0 deletions Marlin/src/core/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ template <class L, class R> struct IF<true, L, R> { typedef L type; };
#define MAIN_AXIS_NAMES NUM_AXIS_LIST(X, Y, Z, I, J, K, U, V, W)
#define MAIN_AXIS_NAMES_LC NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w)
#define STR_AXES_MAIN NUM_AXIS_GANG("X", "Y", "Z", STR_I, STR_J, STR_K, STR_U, STR_V, STR_W)
#define MAIN_AXIS_MAP(F) MAP(F, MAIN_AXIS_NAMES)
#define MAIN_AXIS_MAP_LC(F) MAP(F, MAIN_AXIS_NAMES_LC)

#define LOGICAL_AXIS_GANG(N,V...) NUM_AXIS_GANG(V) GANG_ITEM_E(N)
#define LOGICAL_AXIS_CODE(N,V...) NUM_AXIS_CODE(V) CODE_ITEM_E(N)
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/gcode/calibrate/G28.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ void GcodeSuite::G28() {
return;
}

#if ENABLED(G28_F_TEMPORARY_FEEDRATE)
#if ENABLED(EDITABLE_HOMING_FEEDRATE)
REMEMBER(fr, homing_feedrate_mm_m);
if (parser.floatval('F') > 0)
homing_feedrate_mm_m.x = TERN_(HAS_Y_AXIS, homing_feedrate_mm_m.y =) parser.value_linear_units(); // mm/min
Expand Down
100 changes: 100 additions & 0 deletions Marlin/src/gcode/config/M210.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

#include "../../inc/MarlinConfigPre.h"

#if ENABLED(EDITABLE_HOMING_FEEDRATE)

#include "../gcode.h"
#include "../../module/motion.h"

/**
* M210 - Set homing feedrate for one or more axes
* in current units (in/mm) per minute
*
* X[feedrate] Set X axis homing feedrate
* Y[feedrate] Set Y axis homing feedrate
* Z[feedrate] Set Z axis homing feedrate
* I[feedrate] Set I axis homing feedrate
* J[feedrate] Set J axis homing feedrate
* K[feedrate] Set K axis homing feedrate
* U[feedrate] Set U axis homing feedrate
* V[feedrate] Set V axis homing feedrate
* W[feedrate] Set W axis homing feedrate
*
* With no arguments, report the current offsets.
*/
void GcodeSuite::M210() {
if (!parser.seen_any())
return M210_report();

#if HAS_X_AXIS
if (parser.floatval('X') > 0) homing_feedrate_mm_m.x = parser.value_axis_units(X_AXIS);
#endif
#if HAS_Y_AXIS
if (parser.floatval('Y') > 0) homing_feedrate_mm_m.y = parser.value_axis_units(Y_AXIS);
#endif
#if HAS_Z_AXIS
if (parser.floatval('Z') > 0) homing_feedrate_mm_m.z = parser.value_axis_units(Z_AXIS);
#endif
#if HAS_I_AXIS
if (parser.floatval(AXIS4_NAME) > 0) homing_feedrate_mm_m.i = parser.value_axis_units(I_AXIS);
#endif
#if HAS_J_AXIS
if (parser.floatval(AXIS5_NAME) > 0) homing_feedrate_mm_m.j = parser.value_axis_units(J_AXIS);
#endif
#if HAS_K_AXIS
if (parser.floatval(AXIS6_NAME) > 0) homing_feedrate_mm_m.k = parser.value_axis_units(K_AXIS);
#endif
#if HAS_U_AXIS
if (parser.floatval(AXIS7_NAME) > 0) homing_feedrate_mm_m.u = parser.value_axis_units(U_AXIS);
#endif
#if HAS_V_AXIS
if (parser.floatval(AXIS8_NAME) > 0) homing_feedrate_mm_m.v = parser.value_axis_units(V_AXIS);
#endif
#if HAS_W_AXIS
if (parser.floatval(AXIS9_NAME) > 0) homing_feedrate_mm_m.w = parser.value_axis_units(W_AXIS);
#endif
}

void GcodeSuite::M210_report(const bool forReplay/*=true*/) {
TERN_(MARLIN_SMALL_BUILD, return);

report_heading_etc(forReplay, F(STR_HOMING_FEEDRATE));

SERIAL_ECHOPGM(" M210");
SERIAL_ECHOLNPGM_P(
LIST_N(DOUBLE(NUM_AXES)
, SP_X_STR, X_AXIS_UNIT(homing_feedrate_mm_m.x)
, SP_Y_STR, Y_AXIS_UNIT(homing_feedrate_mm_m.y)
, SP_Z_STR, Z_AXIS_UNIT(homing_feedrate_mm_m.z)
, SP_I_STR, I_AXIS_UNIT(homing_feedrate_mm_m.i)
, SP_J_STR, J_AXIS_UNIT(homing_feedrate_mm_m.j)
, SP_K_STR, K_AXIS_UNIT(homing_feedrate_mm_m.k)
, SP_U_STR, U_AXIS_UNIT(homing_feedrate_mm_m.u)
, SP_V_STR, V_AXIS_UNIT(homing_feedrate_mm_m.v)
, SP_W_STR, W_AXIS_UNIT(homing_feedrate_mm_m.w)
)
);
}

#endif // EDITABLE_HOMING_FEEDRATE
4 changes: 4 additions & 0 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
#endif
#endif

#if ENABLED(EDITABLE_HOMING_FEEDRATE)
case 210: M210(); break; // M210: Set the homing feedrate
#endif

#if HAS_SOFTWARE_ENDSTOPS
case 211: M211(); break; // M211: Enable, Disable, and/or Report software endstops
#endif
Expand Down
6 changes: 6 additions & 0 deletions Marlin/src/gcode/gcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
* M208 - Set Recover (unretract) Additional (!) Length: S<length> and Feedrate: F<units/min>. (Requires FWRETRACT)
* M209 - Turn Automatic Retract Detection on/off: S<0|1> (For slicers that don't support G10/11). (Requires FWRETRACT_AUTORETRACT)
Every normal extrude-only move will be classified as retract depending on the direction.
* M210 - Set or Report the homing feedrate (Requires EDITABLE_HOMING_FEEDRATE)
* M211 - Enable, Disable, and/or Report software endstops: S<0|1> (Requires MIN_SOFTWARE_ENDSTOPS or MAX_SOFTWARE_ENDSTOPS)
* M217 - Set filament swap parameters: "M217 S<length> P<feedrate> R<feedrate>". (Requires SINGLENOZZLE)
* M218 - Set/get a tool offset: "M218 T<index> X<offset> Y<offset>". (Requires 2 or more extruders)
Expand Down Expand Up @@ -897,6 +898,11 @@ class GcodeSuite {
#endif
#endif

#if ENABLED(EDITABLE_HOMING_FEEDRATE)
static void M210();
static void M210_report(const bool forReplay=true);
#endif

static void M211();
static void M211_report(const bool forReplay=true);

Expand Down
3 changes: 3 additions & 0 deletions Marlin/src/gcode/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ class GCodeParser {
#define LINEAR_UNIT(V) parser.mm_to_linear_unit(V)
#define VOLUMETRIC_UNIT(V) parser.mm_to_volumetric_unit(V)

#define X_AXIS_UNIT LINEAR_UNIT
#define Y_AXIS_UNIT LINEAR_UNIT
#define Z_AXIS_UNIT LINEAR_UNIT
#define I_AXIS_UNIT(V) TERN(AXIS4_ROTATES, (V), LINEAR_UNIT(V))
#define J_AXIS_UNIT(V) TERN(AXIS5_ROTATES, (V), LINEAR_UNIT(V))
#define K_AXIS_UNIT(V) TERN(AXIS6_ROTATES, (V), LINEAR_UNIT(V))
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_de.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ namespace LanguageNarrow_de {
LSTR MSG_PROGRESS_BAR_TEST = _UxGT("Statusbalken-Test");
LSTR MSG_HOMING = _UxGT("Homing");
LSTR MSG_AUTO_HOME = _UxGT("Auto Home");
LSTR MSG_AUTO_HOME_A = _UxGT("Home @");
LSTR MSG_AUTO_HOME_N = _UxGT("Home @");
LSTR MSG_AUTO_HOME_X = _UxGT("Home X");
LSTR MSG_AUTO_HOME_Y = _UxGT("Home Y");
LSTR MSG_AUTO_HOME_Z = _UxGT("Home Z");
Expand Down
4 changes: 3 additions & 1 deletion Marlin/src/lcd/language/language_en.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,11 @@ namespace LanguageNarrow_en {
LSTR MSG_ENDSTOP_TEST = _UxGT("Endstop Test");
LSTR MSG_Z_PROBE = _UxGT("Z Probe");
LSTR MSG_HOMING = _UxGT("Homing");
LSTR MSG_HOMING_FEEDRATE = _UxGT("Homing Feed Rate");
LSTR MSG_HOMING_FEEDRATE_N = _UxGT("@ Homing Feed Rate");
LSTR MSG_AUTO_HOME = _UxGT("Auto Home");
LSTR MSG_HOME_ALL = _UxGT("Home All");
LSTR MSG_AUTO_HOME_A = _UxGT("Home @");
LSTR MSG_AUTO_HOME_N = _UxGT("Home @");
LSTR MSG_AUTO_HOME_X = _UxGT("Home X");
LSTR MSG_AUTO_HOME_Y = _UxGT("Home Y");
LSTR MSG_AUTO_HOME_Z = _UxGT("Home Z");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_fr.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ namespace LanguageNarrow_fr {
LSTR MSG_PROGRESS_BAR_TEST = _UxGT("Test barre progress.");
LSTR MSG_HOMING = _UxGT("Origine");
LSTR MSG_AUTO_HOME = _UxGT("Origine auto");
LSTR MSG_AUTO_HOME_A = _UxGT("Origine @ auto");
LSTR MSG_AUTO_HOME_N = _UxGT("Origine @ auto");
LSTR MSG_AUTO_HOME_X = _UxGT("Origine X auto");
LSTR MSG_AUTO_HOME_Y = _UxGT("Origine Y auto");
LSTR MSG_AUTO_HOME_Z = _UxGT("Origine Z auto");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_fr_na.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ namespace LanguageNarrow_fr_na {
LSTR MSG_PROGRESS_BAR_TEST = _UxGT("Test barre progress.");
LSTR MSG_HOMING = _UxGT("Origine");
LSTR MSG_AUTO_HOME = _UxGT("Origine auto");
LSTR MSG_AUTO_HOME_A = _UxGT("Origine @ auto");
LSTR MSG_AUTO_HOME_N = _UxGT("Origine @ auto");
LSTR MSG_AUTO_HOME_X = _UxGT("Origine X auto");
LSTR MSG_AUTO_HOME_Y = _UxGT("Origine Y auto");
LSTR MSG_AUTO_HOME_Z = _UxGT("Origine Z auto");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_hu.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace LanguageNarrow_hu {
LSTR MSG_DEBUG_MENU = _UxGT("Hiba Menü");
LSTR MSG_PROGRESS_BAR_TEST = _UxGT("Haladás sáv teszt");
LSTR MSG_AUTO_HOME = _UxGT("X-Y-Z auto kezdöpont");
LSTR MSG_AUTO_HOME_A = _UxGT("Kezdö @");
LSTR MSG_AUTO_HOME_N = _UxGT("Kezdö @");
LSTR MSG_AUTO_HOME_X = _UxGT("X kezdöpont");
LSTR MSG_AUTO_HOME_Y = _UxGT("Y kezdöpont");
LSTR MSG_AUTO_HOME_Z = _UxGT("Z kezdöpont");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_it.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ namespace LanguageNarrow_it {
LSTR MSG_Z_PROBE = _UxGT("Sonda Z");
LSTR MSG_HOMING = _UxGT("Azzeramento");
LSTR MSG_AUTO_HOME = _UxGT("Auto home");
LSTR MSG_AUTO_HOME_A = _UxGT("Home @");
LSTR MSG_AUTO_HOME_N = _UxGT("Home @");
LSTR MSG_AUTO_HOME_X = _UxGT("Home X");
LSTR MSG_AUTO_HOME_Y = _UxGT("Home Y");
LSTR MSG_AUTO_HOME_Z = _UxGT("Home Z");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_ru.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ namespace LanguageNarrow_ru {
LSTR MSG_DEBUG_MENU = _UxGT("Меню отладки");
LSTR MSG_PROGRESS_BAR_TEST = _UxGT("Тест индикатора");
LSTR MSG_AUTO_HOME = _UxGT("Парковка XYZ");
LSTR MSG_AUTO_HOME_A = _UxGT("Парковка @");
LSTR MSG_AUTO_HOME_N = _UxGT("Парковка @");
LSTR MSG_AUTO_HOME_X = _UxGT("Парковка X");
LSTR MSG_AUTO_HOME_Y = _UxGT("Парковка Y");
LSTR MSG_AUTO_HOME_Z = _UxGT("Парковка Z");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_sk.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ namespace LanguageNarrow_sk {
LSTR MSG_PROGRESS_BAR_TEST = _UxGT("Test uk. priebehu");
LSTR MSG_HOMING = _UxGT("Parkovanie");
LSTR MSG_AUTO_HOME = _UxGT("Domovská pozícia");
LSTR MSG_AUTO_HOME_A = _UxGT("Domov os @");
LSTR MSG_AUTO_HOME_N = _UxGT("Domov os @");
LSTR MSG_AUTO_HOME_X = _UxGT("Domov os X");
LSTR MSG_AUTO_HOME_Y = _UxGT("Domov os Y");
LSTR MSG_AUTO_HOME_Z = _UxGT("Domov os Z");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_tr.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ namespace LanguageNarrow_tr {
LSTR MSG_Z_PROBE = _UxGT("Z Probe");
LSTR MSG_HOMING = _UxGT("Sıfırlanıyor");
LSTR MSG_AUTO_HOME = _UxGT("Eksenleri Sıfırla");
LSTR MSG_AUTO_HOME_A = _UxGT("@ Sıfırla");
LSTR MSG_AUTO_HOME_N = _UxGT("@ Sıfırla");
LSTR MSG_AUTO_HOME_X = _UxGT("X Sıfırla");
LSTR MSG_AUTO_HOME_Y = _UxGT("Y Sıfırla");
LSTR MSG_AUTO_HOME_Z = _UxGT("Z Sıfırla");
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/language/language_uk.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace LanguageNarrow_uk {
LSTR MSG_DEBUG_MENU = _UxGT("Меню Debug");
LSTR MSG_PROGRESS_BAR_TEST = _UxGT("Тест лінії прогр.");
LSTR MSG_AUTO_HOME = _UxGT("Авто паркування");
LSTR MSG_AUTO_HOME_A = _UxGT("Паркування @");
LSTR MSG_AUTO_HOME_N = _UxGT("Паркування @");
LSTR MSG_AUTO_HOME_X = _UxGT("Паркування X");
LSTR MSG_AUTO_HOME_Y = _UxGT("Паркування Y");
LSTR MSG_AUTO_HOME_Z = _UxGT("Паркування Z");
Expand Down
31 changes: 31 additions & 0 deletions Marlin/src/lcd/menu/menu_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,33 @@ void menu_advanced_settings();

#endif

#if ENABLED(EDITABLE_HOMING_FEEDRATE)

#include "../../module/motion.h"
#include "../../module/planner.h"
#include "../../gcode/parser.h"

// Edit homing feedrates in inches- or degrees- or mm-per-minute
void menu_homing_feedrate() {
START_MENU();
BACK_ITEM(MSG_HOMING_FEEDRATE);

#define _EDIT_HOMING_FR(A) do{ \
const float maxfr = MMS_TO_MMM(planner.settings.max_feedrate_mm_s[_AXIS(A)]); \
editable.decimal = A##_AXIS_UNIT(homing_feedrate_mm_m.A); \
EDIT_ITEM(float61, MSG_HOMING_FEEDRATE_N, &editable.decimal, \
A##_AXIS_UNIT(10), A##_AXIS_UNIT(maxfr), []{ \
homing_feedrate_mm_m.A = parser.axis_value_to_mm(_AXIS(A), editable.decimal); \
}); \
}while(0);

MAIN_AXIS_MAP(_EDIT_HOMING_FR);

END_MENU();
}

#endif

#if HAS_PREHEAT && DISABLED(SLIM_LCD_MENUS)

void _menu_configuration_preheat_settings() {
Expand Down Expand Up @@ -629,6 +656,10 @@ void menu_configuration() {
#endif
#endif

#if ENABLED(EDITABLE_HOMING_FEEDRATE)
SUBMENU(MSG_HOMING_FEEDRATE, menu_homing_feedrate);
#endif

#if ENABLED(FWRETRACT)
SUBMENU(MSG_RETRACT, menu_config_retract);
#endif
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/lcd/menu/menu_motion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ void menu_move() {
END_MENU();
}

#define _HOME_ITEM(N) GCODES_ITEM_N(N##_AXIS, MSG_AUTO_HOME_A, F("G28" STR_##N));
#define _HOME_ITEM(N) GCODES_ITEM_N(N##_AXIS, MSG_AUTO_HOME_N, F("G28" STR_##N));

#if ENABLED(INDIVIDUAL_AXIS_HOMING_SUBMENU)
//
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/module/motion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ xyze_pos_t destination; // {0}
#endif
feedRate_t feedrate_mm_s = MMM_TO_MMS(DEFAULT_FEEDRATE_MM_M);
int16_t feedrate_percentage = 100;
#if ENABLED(G28_F_TEMPORARY_FEEDRATE)
#if ENABLED(EDITABLE_HOMING_FEEDRATE)
xyz_feedrate_t homing_feedrate_mm_m = HOMING_FEEDRATE_MM_M;
#endif

Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/module/motion.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ extern xyz_pos_t cartes;
* Feed rates are often configured with mm/m
* but the planner and stepper like mm/s units.
*/
#if ENABLED(G28_F_TEMPORARY_FEEDRATE)
#if ENABLED(EDITABLE_HOMING_FEEDRATE)
extern xyz_feedrate_t homing_feedrate_mm_m;
#else
constexpr xyz_feedrate_t homing_feedrate_mm_m = HOMING_FEEDRATE_MM_M;
Expand Down
Loading

0 comments on commit a934f40

Please sign in to comment.