allow long int for axis length
parent
5aa2e7b9ee
commit
6c0397c441
|
@ -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()", () => {
|
||||
|
|
|
@ -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("<HomingAndCalibration />", () => {
|
||||
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(<HomingAndCalibration
|
||||
dispatch={dispatch} bot={bot} />);
|
||||
const e = { currentTarget: { value: provided } } as
|
||||
React.SyntheticEvent<HTMLInputElement>;
|
||||
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();
|
||||
});
|
||||
});
|
|
@ -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} />
|
||||
<NumericMCUInputGroup
|
||||
name={t("Timeout after (seconds)")}
|
||||
tooltip={t(ToolTips.TIMEOUT_AFTER)}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { BotState } from "../interfaces";
|
||||
import { McuParamName, McuParams } from "farmbot/dist";
|
||||
import { IntegerSize } from "../../util";
|
||||
|
||||
export interface HomingRowProps {
|
||||
hardware: McuParams;
|
||||
|
@ -37,6 +38,7 @@ export interface NumericMCUInputGroupProps {
|
|||
x: McuParamName;
|
||||
y: McuParamName;
|
||||
z: McuParamName;
|
||||
intSize?: IntegerSize;
|
||||
}
|
||||
|
||||
export interface MotorsProps {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { warning } from "farmbot-toastr";
|
|||
import { McuInputBoxProps } from "../interfaces";
|
||||
import { updateMCU } from "../actions";
|
||||
import { BlurableInput } from "../../ui/index";
|
||||
import { clampUnsignedInteger } from "../../util";
|
||||
import { clampUnsignedInteger, IntegerSize } from "../../util";
|
||||
import { t } from "i18next";
|
||||
|
||||
export class McuInputBox extends React.Component<McuInputBoxProps, {}> {
|
||||
|
@ -16,25 +16,31 @@ export class McuInputBox extends React.Component<McuInputBoxProps, {}> {
|
|||
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<HTMLInputElement>) => {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <Row>
|
||||
<Col xs={6}>
|
||||
<label>
|
||||
|
@ -19,19 +19,22 @@ export function NumericMCUInputGroup(props: NumericMCUInputGroupProps) {
|
|||
<McuInputBox
|
||||
setting={x}
|
||||
bot={bot}
|
||||
dispatch={dispatch} />
|
||||
dispatch={dispatch}
|
||||
intSize={intSize} />
|
||||
</Col>
|
||||
<Col xs={2}>
|
||||
<McuInputBox
|
||||
setting={y}
|
||||
bot={bot}
|
||||
dispatch={dispatch} />
|
||||
dispatch={dispatch}
|
||||
intSize={intSize} />
|
||||
</Col>
|
||||
<Col xs={2}>
|
||||
<McuInputBox
|
||||
setting={z}
|
||||
bot={bot}
|
||||
dispatch={dispatch} />
|
||||
dispatch={dispatch}
|
||||
intSize={intSize} />
|
||||
</Col>
|
||||
</Row>;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -323,7 +323,8 @@ export function attachToRoot<P>(type: React.ComponentClass<P>,
|
|||
}
|
||||
|
||||
/** 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 };
|
||||
|
|
Loading…
Reference in New Issue