From 6c0397c44187961edbe55edb4435ccbf7ef05a08 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Sat, 16 Dec 2017 16:16:19 -0800 Subject: [PATCH] allow long int for axis length --- webpack/__tests__/test_util.ts | 22 +++++--- .../__tests__/homing_and_calibration_test.tsx | 50 +++++++++++++++++++ .../homing_and_calibration.tsx | 10 +++- webpack/devices/components/interfaces.ts | 2 + webpack/devices/components/mcu_input_box.tsx | 38 ++++++++------ .../components/numeric_mcu_input_group.tsx | 11 ++-- webpack/devices/interfaces.ts | 2 + webpack/util.ts | 19 +++++-- 8 files changed, 122 insertions(+), 32 deletions(-) create mode 100644 webpack/devices/components/hardware_settings/__tests__/homing_and_calibration_test.tsx diff --git a/webpack/__tests__/test_util.ts b/webpack/__tests__/test_util.ts index 94d5300f6..7b5536e63 100644 --- a/webpack/__tests__/test_util.ts +++ b/webpack/__tests__/test_util.ts @@ -14,7 +14,8 @@ import { move, shortRevision, clampUnsignedInteger, - isUndefined + isUndefined, + IntegerSize } from "../util"; describe("util", () => { describe("safeStringFetch", () => { @@ -233,17 +234,22 @@ describe("shortRevision()", () => { describe("clampUnsignedInteger()", () => { function clampTest( - input: string, output: number | undefined, message: string) { - it(message, () => { - const result = clampUnsignedInteger(input); + input: string, + output: number | undefined, + message: string, + size: IntegerSize) { + it(`${size}: ${message}`, () => { + const result = clampUnsignedInteger(input, size); expect(result.outcome).toEqual(message); expect(result.result).toEqual(output); }); } - clampTest("nope", undefined, "malformed"); - clampTest("100000", 32000, "high"); - clampTest("-100000", 0, "low"); - clampTest("1000", 1000, "ok"); + clampTest("nope", undefined, "malformed", "short"); + clampTest("100000", 32000, "high", "short"); + clampTest("-100000", 0, "low", "short"); + clampTest("1000", 1000, "ok", "short"); + clampTest("1000000", 1000000, "ok", "long"); + clampTest("-1000000", 0, "low", "long"); }); describe("isUndefined()", () => { diff --git a/webpack/devices/components/hardware_settings/__tests__/homing_and_calibration_test.tsx b/webpack/devices/components/hardware_settings/__tests__/homing_and_calibration_test.tsx new file mode 100644 index 000000000..2aa497e71 --- /dev/null +++ b/webpack/devices/components/hardware_settings/__tests__/homing_and_calibration_test.tsx @@ -0,0 +1,50 @@ +jest.mock("../../../actions", () => ({ + updateMCU: jest.fn() +})); + +const mockWarn = jest.fn(); +jest.mock("farmbot-toastr", () => ({ warning: mockWarn })); + +import * as React from "react"; +import { mount } from "enzyme"; +import { HomingAndCalibration } from "../homing_and_calibration"; +import { bot } from "../../../../__test_support__/fake_state/bot"; +import { updateMCU } from "../../../actions"; + +describe("", () => { + beforeEach(function () { + jest.clearAllMocks(); + }); + + function testAxisLengthInput( + fw: string, provided: string, expected: string) { + const dispatch = jest.fn(); + bot.controlPanelState.homing_and_calibration = true; + bot.hardware.informational_settings.firmware_version = fw; + const result = mount(); + const e = { currentTarget: { value: provided } } as + React.SyntheticEvent; + const input = result.find("input").first().props(); + input.onChange && input.onChange(e); + input.onSubmit && input.onSubmit(e); + expect(updateMCU) + .toHaveBeenCalledWith("movement_axis_nr_steps_x", expected); + } + it("short int", () => { + testAxisLengthInput("5.0.0", "100000", "32000"); + expect(mockWarn) + .toHaveBeenCalledWith("Maximum input is 32,000. Rounding down."); + }); + + it("long int: too long", () => { + testAxisLengthInput("6.0.0", "10000000000", "2000000000"); + expect(mockWarn) + .toHaveBeenCalledWith("Maximum input is 2,000,000,000. Rounding down."); + }); + + it("long int: ok", () => { + testAxisLengthInput("6.0.0", "100000", "100000"); + expect(mockWarn).not.toHaveBeenCalled(); + }); +}); diff --git a/webpack/devices/components/hardware_settings/homing_and_calibration.tsx b/webpack/devices/components/hardware_settings/homing_and_calibration.tsx index 3a4ace83d..e105a6815 100644 --- a/webpack/devices/components/hardware_settings/homing_and_calibration.tsx +++ b/webpack/devices/components/hardware_settings/homing_and_calibration.tsx @@ -10,13 +10,20 @@ import { enabledAxisMap } from "../axis_tracking_status"; import { HomingAndCalibrationProps } from "../interfaces"; import { Header } from "./header"; import { Collapse } from "@blueprintjs/core"; +import { minFwVersionCheck } from "../../../util"; export function HomingAndCalibration(props: HomingAndCalibrationProps) { const { dispatch, bot } = props; const { mcu_params } = bot.hardware; + const { firmware_version } = bot.hardware.informational_settings; const { homing_and_calibration } = props.bot.controlPanelState; + const axisLengthIntSize = + minFwVersionCheck(firmware_version, "6.0.0") + ? "long" + : "short"; + /** * Tells us if X/Y/Z have a means of checking their position. * FARMBOT WILL CRASH INTO WALLS IF THIS IS WRONG! BE CAREFUL. @@ -76,7 +83,8 @@ export function HomingAndCalibration(props: HomingAndCalibrationProps) { y={"movement_axis_nr_steps_y"} z={"movement_axis_nr_steps_z"} bot={bot} - dispatch={dispatch} /> + dispatch={dispatch} + intSize={axisLengthIntSize} /> { @@ -16,25 +16,31 @@ export class McuInputBox extends React.Component { return _.isUndefined(v) ? "" : (v || 0).toString(); } + clampInputAndWarn = (input: string, intSize: IntegerSize): number => { + const result = clampUnsignedInteger(input, intSize); + const max = intSize === "long" ? "2,000,000,000" : "32,000"; + switch (result.outcome) { + case "ok": + break; + case "high": + warning(t(`Maximum input is ${max}. Rounding down.`)); + break; + case "low": + warning(t("Must be a positive number. Rounding up to 0.")); + break; + default: + warning(t(`Please enter a number between 0 and ${max}`)); + throw new Error("Bad input in mcu_input_box. Impossible?"); + } + return result.result; + } + commit = (e: React.SyntheticEvent) => { const { value } = e.currentTarget; const actuallyDifferent = this.value !== value; if (actuallyDifferent) { - const result = clampUnsignedInteger(value); - switch (result.outcome) { - case "ok": - break; - case "high": - warning(t("Maximum input is 32,000. Rounding down.")); - break; - case "low": - warning(t("Must be a positive number. Rounding up to 0.")); - break; - default: - warning(t("Please enter a number between 0 and 32,000")); - throw new Error("Bad input in mcu_input_box. Impossible?"); - } - this.props.dispatch(updateMCU(this.key, result.result.toString())); + const result = this.clampInputAndWarn(value, this.props.intSize); + this.props.dispatch(updateMCU(this.key, result.toString())); } } diff --git a/webpack/devices/components/numeric_mcu_input_group.tsx b/webpack/devices/components/numeric_mcu_input_group.tsx index 2d0683961..61e940f6f 100644 --- a/webpack/devices/components/numeric_mcu_input_group.tsx +++ b/webpack/devices/components/numeric_mcu_input_group.tsx @@ -7,7 +7,7 @@ import { Col } from "../../ui/index"; export function NumericMCUInputGroup(props: NumericMCUInputGroupProps) { - const { bot, dispatch, tooltip, name, x, y, z } = props; + const { bot, dispatch, tooltip, name, x, y, z, intSize } = props; return ; } diff --git a/webpack/devices/interfaces.ts b/webpack/devices/interfaces.ts index 82d3f42cb..f62ffbe76 100644 --- a/webpack/devices/interfaces.ts +++ b/webpack/devices/interfaces.ts @@ -18,6 +18,7 @@ import { TaggedUser } from "../resources/tagged_resources"; import { WD_ENV } from "../farmware/weed_detector/remote_env/interfaces"; import { EncoderDisplay } from "../controls/interfaces"; import { ConnectionStatus } from "../connectivity/interfaces"; +import { IntegerSize } from "../util"; export interface Props { userToApi: ConnectionStatus | undefined; @@ -128,6 +129,7 @@ export interface McuInputBoxProps { bot: BotState; setting: McuParamName; dispatch: Function; + intSize?: IntegerSize; } export interface EStopButtonProps { diff --git a/webpack/util.ts b/webpack/util.ts index b63b841cd..bf72db39a 100644 --- a/webpack/util.ts +++ b/webpack/util.ts @@ -323,7 +323,8 @@ export function attachToRoot

(type: React.ComponentClass

, } /** The firmware will have an integer overflow if you don't check this one. */ -const MAX_INPUT = 32000; +const MAX_SHORT_INPUT = 32000; +const MAX_LONG_INPUT = 2000000000; const MIN_INPUT = 0; interface High { outcome: "high"; result: number; } @@ -332,14 +333,26 @@ interface Malformed { outcome: "malformed"; result: undefined; } interface Ok { outcome: "ok", result: number; } export type ClampResult = High | Low | Malformed | Ok; +export type IntegerSize = "short" | "long" | undefined; + /** Handle all the possible ways a user could give us bad data or cause an * integer overflow in the firmware. */ -export function clampUnsignedInteger(input: string): ClampResult { +export function clampUnsignedInteger( + input: string, size: IntegerSize): ClampResult { const result = Math.round(parseInt(input, 10)); + const maxInput = () => { + switch (size) { + case "long": + return MAX_LONG_INPUT; + case "short": + default: + return MAX_SHORT_INPUT; + } + }; // Clamp to prevent overflow. if (_.isNaN(result)) { return { outcome: "malformed", result: undefined }; } - if (result > MAX_INPUT) { return { outcome: "high", result: MAX_INPUT }; } + if (result > maxInput()) { return { outcome: "high", result: maxInput() }; } if (result < MIN_INPUT) { return { outcome: "low", result: MIN_INPUT }; } return { outcome: "ok", result };