sequence step warnings
parent
f3caa88f73
commit
465fa266eb
|
@ -0,0 +1,11 @@
|
|||
import { HardwareFlags } from "../sequences/interfaces";
|
||||
|
||||
export const fakeHardwareFlags = (): HardwareFlags => {
|
||||
return {
|
||||
findHomeEnabled: { x: false, y: false, z: false },
|
||||
stopAtHome: { x: false, y: false, z: false },
|
||||
stopAtMax: { x: false, y: false, z: false },
|
||||
negativeOnly: { x: false, y: false, z: false },
|
||||
axisLength: { x: 0, y: 0, z: 0 },
|
||||
};
|
||||
};
|
|
@ -409,6 +409,11 @@ export namespace Content {
|
|||
trim(`No Sequence selected. Click one in the Sequences panel to edit,
|
||||
or click "+" to create a new one.`);
|
||||
|
||||
export const END_DETECTION_DISABLED =
|
||||
trim(`This command will not execute correctly because you do not have
|
||||
encoders or endstops enabled for the chosen axis. Enable endstops or
|
||||
encoders from the Device page for: `);
|
||||
|
||||
// Regimens
|
||||
export const NO_REGIMEN_SELECTED =
|
||||
trim(`No Regimen selected. Click one in the Regimens panel to edit, or
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
.pt-popover-target {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
@ -103,6 +102,13 @@
|
|||
.help {
|
||||
float: right;
|
||||
}
|
||||
.step-warning {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
.pt-popover-target {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.step-control {
|
||||
|
|
|
@ -25,6 +25,8 @@ import {
|
|||
} from "../../__test_support__/resource_index_builder";
|
||||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import { destroy } from "../../api/crud";
|
||||
import { fakeHardwareFlags } from "../../__test_support__/sequence_hardware_settings";
|
||||
|
||||
|
||||
describe("<SequenceEditorMiddleActive/>", () => {
|
||||
function fakeProps(): ActiveMiddleProps {
|
||||
|
@ -34,7 +36,8 @@ describe("<SequenceEditorMiddleActive/>", () => {
|
|||
resources: buildResourceIndex(FAKE_RESOURCES).index,
|
||||
syncStatus: "synced",
|
||||
consistent: true,
|
||||
autoSyncEnabled: false
|
||||
autoSyncEnabled: false,
|
||||
hardwareFlags: fakeHardwareFlags()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
FAKE_RESOURCES, buildResourceIndex
|
||||
} from "../../__test_support__/resource_index_builder";
|
||||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import { fakeHardwareFlags } from "../../__test_support__/sequence_hardware_settings";
|
||||
|
||||
describe("<SequenceEditorMiddle/>", () => {
|
||||
function fakeProps(): SequenceEditorMiddleProps {
|
||||
|
@ -15,7 +16,8 @@ describe("<SequenceEditorMiddle/>", () => {
|
|||
resources: buildResourceIndex(FAKE_RESOURCES).index,
|
||||
syncStatus: "synced",
|
||||
consistent: true,
|
||||
autoSyncEnabled: false
|
||||
autoSyncEnabled: false,
|
||||
hardwareFlags: fakeHardwareFlags()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import { auth } from "../../__test_support__/fake_state/token";
|
||||
import { ToolTips } from "../../constants";
|
||||
import { fakeHardwareFlags } from "../../__test_support__/sequence_hardware_settings";
|
||||
|
||||
describe("<Sequences/>", () => {
|
||||
function fakeProps(): Props {
|
||||
|
@ -23,7 +24,8 @@ describe("<Sequences/>", () => {
|
|||
syncStatus: "synced",
|
||||
auth,
|
||||
consistent: true,
|
||||
autoSyncEnabled: false
|
||||
autoSyncEnabled: false,
|
||||
hardwareFlags: fakeHardwareFlags()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,17 +6,19 @@ import { StepDragger } from "../draggable/step_dragger";
|
|||
import { renderCeleryNode } from "./step_tiles/index";
|
||||
import { ResourceIndex } from "../resources/interfaces";
|
||||
import { getStepTag } from "../resources/sequence_tagging";
|
||||
import { HardwareFlags } from "./interfaces";
|
||||
|
||||
interface AllStepsProps {
|
||||
sequence: TaggedSequence;
|
||||
onDrop(index: number, key: string): void;
|
||||
dispatch: Function;
|
||||
resources: ResourceIndex;
|
||||
hardwareFlags?: HardwareFlags;
|
||||
}
|
||||
|
||||
export class AllSteps extends React.Component<AllStepsProps, {}> {
|
||||
render() {
|
||||
const { sequence, onDrop, dispatch } = this.props;
|
||||
const { sequence, onDrop, dispatch, hardwareFlags } = this.props;
|
||||
const items = (sequence.body.body || [])
|
||||
.map((currentStep: SequenceBodyItem, index, arr) => {
|
||||
/** HACK: React's diff algorithm (probably?) can't keep track of steps
|
||||
|
@ -39,7 +41,8 @@ export class AllSteps extends React.Component<AllStepsProps, {}> {
|
|||
index,
|
||||
dispatch: dispatch,
|
||||
currentSequence: sequence,
|
||||
resources: this.props.resources
|
||||
resources: this.props.resources,
|
||||
hardwareFlags
|
||||
})}
|
||||
</div>
|
||||
</StepDragger>
|
||||
|
|
|
@ -5,13 +5,22 @@ import {
|
|||
SequenceBodyItem,
|
||||
LegalArgString,
|
||||
SyncStatus,
|
||||
ALLOWED_CHANNEL_NAMES
|
||||
ALLOWED_CHANNEL_NAMES,
|
||||
Xyz
|
||||
} from "farmbot";
|
||||
import { StepMoveDataXfer, StepSpliceDataXfer } from "../draggable/interfaces";
|
||||
import { TaggedSequence } from "../resources/tagged_resources";
|
||||
import { ResourceIndex } from "../resources/interfaces";
|
||||
import { JSXChildren } from "../util";
|
||||
|
||||
export interface HardwareFlags {
|
||||
findHomeEnabled: Record<Xyz, boolean>;
|
||||
stopAtHome: Record<Xyz, boolean>;
|
||||
stopAtMax: Record<Xyz, boolean>;
|
||||
negativeOnly: Record<Xyz, boolean>;
|
||||
axisLength: Record<Xyz, number>;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
dispatch: Function;
|
||||
sequences: TaggedSequence[];
|
||||
|
@ -21,6 +30,7 @@ export interface Props {
|
|||
syncStatus: SyncStatus;
|
||||
consistent: boolean;
|
||||
autoSyncEnabled: boolean;
|
||||
hardwareFlags: HardwareFlags;
|
||||
}
|
||||
|
||||
export interface SequenceEditorMiddleProps {
|
||||
|
@ -30,6 +40,7 @@ export interface SequenceEditorMiddleProps {
|
|||
syncStatus: SyncStatus;
|
||||
consistent: boolean;
|
||||
autoSyncEnabled: boolean;
|
||||
hardwareFlags: HardwareFlags;
|
||||
}
|
||||
|
||||
export interface ActiveMiddleProps extends SequenceEditorMiddleProps {
|
||||
|
@ -146,4 +157,5 @@ export interface StepParams {
|
|||
dispatch: Function;
|
||||
index: number;
|
||||
resources: ResourceIndex;
|
||||
hardwareFlags?: HardwareFlags;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ export class SequenceEditorMiddle
|
|||
dispatch,
|
||||
sequence,
|
||||
resources,
|
||||
syncStatus
|
||||
syncStatus,
|
||||
hardwareFlags
|
||||
} = this.props;
|
||||
if (sequence && isTaggedSequence(sequence)) {
|
||||
return <SequenceEditorMiddleActive
|
||||
|
@ -20,7 +21,8 @@ export class SequenceEditorMiddle
|
|||
resources={resources}
|
||||
syncStatus={syncStatus}
|
||||
consistent={true}
|
||||
autoSyncEnabled={false} />;
|
||||
autoSyncEnabled={false}
|
||||
hardwareFlags={hardwareFlags} />;
|
||||
} else {
|
||||
return <SequenceEditorMiddleInactive />;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,8 @@ export class Sequences extends React.Component<Props, {}> {
|
|||
sequence={this.props.sequence}
|
||||
resources={this.props.resources}
|
||||
consistent={this.props.consistent}
|
||||
autoSyncEnabled={this.props.autoSyncEnabled} />
|
||||
autoSyncEnabled={this.props.autoSyncEnabled}
|
||||
hardwareFlags={this.props.hardwareFlags} />
|
||||
</div>
|
||||
</Col>
|
||||
<Col sm={3}>
|
||||
|
|
|
@ -1,16 +1,47 @@
|
|||
import { Everything } from "../interfaces";
|
||||
import { Props } from "./interfaces";
|
||||
import { Props, HardwareFlags } from "./interfaces";
|
||||
import {
|
||||
selectAllSequences,
|
||||
findSequence
|
||||
} from "../resources/selectors";
|
||||
import { getStepTag } from "../resources/sequence_tagging";
|
||||
import { enabledAxisMap } from "../devices/components/axis_tracking_status";
|
||||
|
||||
export function mapStateToProps(props: Everything): Props {
|
||||
const uuid = props.resources.consumers.sequences.current;
|
||||
const sequence = uuid ? findSequence(props.resources.index, uuid) : undefined;
|
||||
sequence && (sequence.body.body || []).map(x => getStepTag(x));
|
||||
|
||||
const hardwareFlags = (): HardwareFlags => {
|
||||
const { mcu_params } = props.bot.hardware;
|
||||
return {
|
||||
findHomeEnabled: enabledAxisMap(mcu_params),
|
||||
stopAtHome: {
|
||||
x: !!mcu_params.movement_stop_at_home_x,
|
||||
y: !!mcu_params.movement_stop_at_home_y,
|
||||
z: !!mcu_params.movement_stop_at_home_z
|
||||
},
|
||||
stopAtMax: {
|
||||
x: !!mcu_params.movement_stop_at_max_x,
|
||||
y: !!mcu_params.movement_stop_at_max_y,
|
||||
z: !!mcu_params.movement_stop_at_max_z
|
||||
},
|
||||
negativeOnly: {
|
||||
x: !!mcu_params.movement_home_up_x,
|
||||
y: !!mcu_params.movement_home_up_y,
|
||||
z: !!mcu_params.movement_home_up_z
|
||||
},
|
||||
axisLength: {
|
||||
x: (mcu_params.movement_axis_nr_steps_x || 0)
|
||||
/ (mcu_params.movement_step_per_mm_x || 1),
|
||||
y: (mcu_params.movement_axis_nr_steps_y || 0)
|
||||
/ (mcu_params.movement_step_per_mm_y || 1),
|
||||
z: (mcu_params.movement_axis_nr_steps_z || 0)
|
||||
/ (mcu_params.movement_step_per_mm_z || 1)
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
dispatch: props.dispatch,
|
||||
sequences: selectAllSequences(props.resources.index),
|
||||
|
@ -23,6 +54,7 @@ export function mapStateToProps(props: Everything): Props {
|
|||
.informational_settings
|
||||
.sync_status || "unknown"),
|
||||
consistent: props.bot.consistent,
|
||||
autoSyncEnabled: !!props.bot.hardware.configuration.auto_sync
|
||||
autoSyncEnabled: !!props.bot.hardware.configuration.auto_sync,
|
||||
hardwareFlags: hardwareFlags(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,9 +4,12 @@ import { mount } from "enzyme";
|
|||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
import { FindHome } from "farmbot/dist";
|
||||
import { emptyState } from "../../../resources/reducer";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../../__test_support__/sequence_hardware_settings";
|
||||
|
||||
describe("<TileFindHome/>", () => {
|
||||
function bootstrapTest() {
|
||||
const fakeProps = () => {
|
||||
const currentStep: FindHome = {
|
||||
kind: "find_home",
|
||||
args: {
|
||||
|
@ -15,19 +18,19 @@ describe("<TileFindHome/>", () => {
|
|||
}
|
||||
};
|
||||
return {
|
||||
component: mount(<TileFindHome
|
||||
currentSequence={fakeSequence()}
|
||||
currentStep={currentStep}
|
||||
dispatch={jest.fn()}
|
||||
index={0}
|
||||
resources={emptyState().index} />)
|
||||
currentSequence: fakeSequence(),
|
||||
currentStep: currentStep,
|
||||
dispatch: jest.fn(),
|
||||
index: 0,
|
||||
resources: emptyState().index,
|
||||
hardwareFlags: fakeHardwareFlags()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
it("renders inputs", () => {
|
||||
const block = bootstrapTest().component;
|
||||
const inputs = block.find("input");
|
||||
const labels = block.find("label");
|
||||
const wrapper = mount(<TileFindHome {...fakeProps() } />);
|
||||
const inputs = wrapper.find("input");
|
||||
const labels = wrapper.find("label");
|
||||
expect(inputs.length).toEqual(5);
|
||||
expect(labels.length).toEqual(4);
|
||||
expect(inputs.first().props().placeholder).toEqual("Find Home");
|
||||
|
@ -40,4 +43,28 @@ describe("<TileFindHome/>", () => {
|
|||
expect(labels.at(3).text()).toContain("Find all");
|
||||
expect(inputs.at(4).props().checked).toBeTruthy();
|
||||
});
|
||||
|
||||
it("doesn't render warning", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.axis = "x";
|
||||
p.hardwareFlags.findHomeEnabled.x = true;
|
||||
const wrapper = mount(<TileFindHome {...p} />);
|
||||
expect(wrapper.text()).not.toContain("Hardware setting conflict.");
|
||||
});
|
||||
|
||||
it("renders warning: all axes", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.axis = "all";
|
||||
p.hardwareFlags.findHomeEnabled.x = false;
|
||||
const wrapper = mount(<TileFindHome {...p} />);
|
||||
expect(wrapper.text()).toContain("Hardware setting conflict.");
|
||||
});
|
||||
|
||||
it("renders warning: one axis", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.axis = "x";
|
||||
p.hardwareFlags.findHomeEnabled.x = false;
|
||||
const wrapper = mount(<TileFindHome {...p} />);
|
||||
expect(wrapper.text()).toContain("Hardware setting conflict.");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,9 +6,10 @@ import { MoveAbsolute, SequenceBodyItem } from "farmbot/dist";
|
|||
import { emptyState } from "../../../resources/reducer";
|
||||
import { buildResourceIndex } from "../../../__test_support__/resource_index_builder";
|
||||
import { SpecialStatus } from "../../../resources/tagged_resources";
|
||||
import { fakeHardwareFlags } from "../../../__test_support__/sequence_hardware_settings";
|
||||
|
||||
describe("<TileMoveAbsolute/>", () => {
|
||||
function bootstrapTest() {
|
||||
const fakeProps = () => {
|
||||
const currentStep: MoveAbsolute = {
|
||||
kind: "move_absolute",
|
||||
args: {
|
||||
|
@ -32,14 +33,14 @@ describe("<TileMoveAbsolute/>", () => {
|
|||
}
|
||||
};
|
||||
return {
|
||||
component: mount(<TileMoveAbsolute
|
||||
currentSequence={fakeSequence()}
|
||||
currentStep={currentStep}
|
||||
dispatch={jest.fn()}
|
||||
index={0}
|
||||
resources={emptyState().index} />)
|
||||
currentSequence: fakeSequence(),
|
||||
currentStep: currentStep,
|
||||
dispatch: jest.fn(),
|
||||
index: 0,
|
||||
resources: emptyState().index,
|
||||
hardwareFlags: fakeHardwareFlags()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function checkField(
|
||||
block: ReactWrapper, position: number, label: string, value: string | number
|
||||
|
@ -51,7 +52,7 @@ describe("<TileMoveAbsolute/>", () => {
|
|||
}
|
||||
|
||||
it("renders inputs", () => {
|
||||
const block = bootstrapTest().component;
|
||||
const block = mount(<TileMoveAbsolute {...fakeProps() } />);
|
||||
const inputs = block.find("input");
|
||||
const labels = block.find("label");
|
||||
const buttons = block.find("button");
|
||||
|
@ -103,4 +104,58 @@ describe("<TileMoveAbsolute/>", () => {
|
|||
|
||||
expect(component.tool).toEqual(tool);
|
||||
});
|
||||
|
||||
const conflictText = "Hardware setting conflict.";
|
||||
|
||||
it("doesn't show setting warning", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = mount(<TileMoveAbsolute {...p} />);
|
||||
expect(wrapper.text()).not.toContain(conflictText);
|
||||
});
|
||||
|
||||
it("doesn't show warning: axis length 0", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.offset.args.x = 10000;
|
||||
p.hardwareFlags.stopAtMax.x = true;
|
||||
p.hardwareFlags.axisLength.x = 0;
|
||||
const wrapper = mount(<TileMoveAbsolute {...p} />);
|
||||
expect(wrapper.text()).not.toContain(conflictText);
|
||||
});
|
||||
|
||||
it("shows warning: too high", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.offset.args.x = 10000;
|
||||
p.hardwareFlags.stopAtMax.x = true;
|
||||
p.hardwareFlags.axisLength.x = 100;
|
||||
const wrapper = mount(<TileMoveAbsolute {...p} />);
|
||||
expect(wrapper.text()).toContain(conflictText);
|
||||
});
|
||||
|
||||
it("shows warning: too high (negativeOnly)", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.offset.args.x = -10000;
|
||||
p.hardwareFlags.stopAtMax.x = true;
|
||||
p.hardwareFlags.negativeOnly.x = true;
|
||||
p.hardwareFlags.axisLength.x = 100;
|
||||
const wrapper = mount(<TileMoveAbsolute {...p} />);
|
||||
expect(wrapper.text()).toContain(conflictText);
|
||||
});
|
||||
|
||||
it("shows warning: too low (negativeOnly)", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.offset.args.x = 10000;
|
||||
p.hardwareFlags.stopAtHome.x = true;
|
||||
p.hardwareFlags.negativeOnly.x = true;
|
||||
const wrapper = mount(<TileMoveAbsolute {...p} />);
|
||||
expect(wrapper.text()).toContain(conflictText);
|
||||
});
|
||||
|
||||
it("shows warning: too low", () => {
|
||||
const p = fakeProps();
|
||||
p.currentStep.args.offset.args.x = -10000;
|
||||
p.hardwareFlags.stopAtHome.x = true;
|
||||
p.hardwareFlags.stopAtMax.x = true;
|
||||
const wrapper = mount(<TileMoveAbsolute {...p} />);
|
||||
expect(wrapper.text()).toContain(conflictText);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { FindHome, ALLOWED_AXIS } from "farmbot";
|
||||
import { StepParams } from "../interfaces";
|
||||
import { FindHome, ALLOWED_AXIS, Xyz } from "farmbot";
|
||||
import { StepParams, HardwareFlags } from "../interfaces";
|
||||
import { TaggedSequence } from "../../resources/tagged_resources";
|
||||
import { ResourceIndex } from "../../resources/interfaces";
|
||||
import { overwrite } from "../../api/crud";
|
||||
import { defensiveClone } from "../../util";
|
||||
import { ToolTips } from "../../constants";
|
||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||
import { ToolTips, Content } from "../../constants";
|
||||
import {
|
||||
StepWrapper, StepHeader, StepContent, StepWarning
|
||||
} from "../step_ui/index";
|
||||
import { Row, Col } from "../../ui/index";
|
||||
import { some } from "lodash";
|
||||
|
||||
export function TileFindHome(props: StepParams) {
|
||||
if (props.currentStep.kind === "find_home") {
|
||||
|
@ -16,9 +19,10 @@ export function TileFindHome(props: StepParams) {
|
|||
currentSequence={props.currentSequence}
|
||||
dispatch={props.dispatch}
|
||||
index={props.index}
|
||||
resources={props.resources} />;
|
||||
resources={props.resources}
|
||||
hardwareFlags={props.hardwareFlags} />;
|
||||
} else {
|
||||
throw new Error("TileFindHome expects send_message");
|
||||
throw new Error("TileFindHome expects find_home");
|
||||
}
|
||||
}
|
||||
interface FindHomeParams {
|
||||
|
@ -27,6 +31,7 @@ interface FindHomeParams {
|
|||
dispatch: Function;
|
||||
index: number;
|
||||
resources: ResourceIndex;
|
||||
hardwareFlags: HardwareFlags | undefined;
|
||||
}
|
||||
|
||||
const AXIS_CHOICES: ALLOWED_AXIS[] = ["x", "y", "z", "all"];
|
||||
|
@ -45,6 +50,36 @@ class InnerFindHome extends React.Component<FindHomeParams, {}> {
|
|||
this.props.dispatch(overwrite(this.props.currentSequence, nextSequence));
|
||||
}
|
||||
|
||||
get settingConflicts(): Record<Xyz, boolean> {
|
||||
const conflicts = { x: false, y: false, z: false };
|
||||
if (this.props.hardwareFlags) {
|
||||
const { axis } = this.props.currentStep.args;
|
||||
const { findHomeEnabled } = this.props.hardwareFlags;
|
||||
switch (axis) {
|
||||
case "x":
|
||||
case "y":
|
||||
case "z":
|
||||
conflicts[axis] = !findHomeEnabled[axis];
|
||||
break;
|
||||
case "all":
|
||||
conflicts.x = !findHomeEnabled.x;
|
||||
conflicts.y = !findHomeEnabled.y;
|
||||
conflicts.z = !findHomeEnabled.z;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
get settingConflictWarning() {
|
||||
const conflictAxes: string[] = [];
|
||||
Object.entries(this.settingConflicts)
|
||||
.map(([label, value]) => {
|
||||
if (value) { conflictAxes.push(label); }
|
||||
});
|
||||
return Content.END_DETECTION_DISABLED + conflictAxes.join(", ");
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dispatch, index, currentStep, currentSequence } = this.props;
|
||||
|
||||
|
@ -56,7 +91,10 @@ class InnerFindHome extends React.Component<FindHomeParams, {}> {
|
|||
currentSequence={currentSequence}
|
||||
currentStep={currentStep}
|
||||
dispatch={dispatch}
|
||||
index={index} />
|
||||
index={index}>
|
||||
{some(this.settingConflicts) &&
|
||||
StepWarning(this.settingConflictWarning)}
|
||||
</StepHeader>
|
||||
<StepContent className={className}>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
|
|
|
@ -29,7 +29,7 @@ import { Xyz } from "../../devices/interfaces";
|
|||
import { TileMoveAbsSelect, InputBox } from "./tile_move_absolute/index";
|
||||
import { ToolTips } from "../../constants";
|
||||
import { extractParent } from "../locals_list";
|
||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||
import { StepWrapper, StepHeader, StepContent, StepWarning } from "../step_ui/index";
|
||||
import { StepInputBox } from "../inputs/step_input_box";
|
||||
|
||||
interface Args {
|
||||
|
@ -118,6 +118,38 @@ export class TileMoveAbsolute extends Component<StepParams, MoveAbsState> {
|
|||
this.updateArgs(_.merge({}, this.args, update));
|
||||
}
|
||||
|
||||
get settingConflicts(): Record<Xyz, boolean> {
|
||||
const conflicts = { x: false, y: false, z: false };
|
||||
if (this.props.hardwareFlags) {
|
||||
const {
|
||||
stopAtHome, stopAtMax, negativeOnly, axisLength
|
||||
} = this.props.hardwareFlags;
|
||||
["x", "y", "z"].map((axis: Xyz) => {
|
||||
const coord = parseFloat(this.getAxisValue(axis));
|
||||
const offset = parseFloat(this.getOffsetValue(axis));
|
||||
const sum = coord + offset;
|
||||
if (stopAtHome[axis]) {
|
||||
conflicts[axis] = negativeOnly[axis] ? sum > 0 : sum < 0;
|
||||
}
|
||||
if (stopAtMax[axis] && axisLength[axis] !== 0) {
|
||||
conflicts[axis] = conflicts[axis] || (negativeOnly[axis]
|
||||
? sum < -axisLength[axis]
|
||||
: sum > axisLength[axis]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
get settingConflictWarning() {
|
||||
const conflictAxes: string[] = [];
|
||||
Object.entries(this.settingConflicts)
|
||||
.map(([label, value]) => {
|
||||
if (value) { conflictAxes.push(label); }
|
||||
});
|
||||
return "Movement out of bounds for: " + conflictAxes.join(", ");
|
||||
}
|
||||
|
||||
render() {
|
||||
const { currentStep, dispatch, index, currentSequence } = this.props;
|
||||
if (currentSequence && !isTaggedSequence(currentSequence)) {
|
||||
|
@ -132,7 +164,10 @@ export class TileMoveAbsolute extends Component<StepParams, MoveAbsState> {
|
|||
currentSequence={currentSequence}
|
||||
currentStep={currentStep}
|
||||
dispatch={dispatch}
|
||||
index={index} />
|
||||
index={index}>
|
||||
{_.some(this.settingConflicts) &&
|
||||
StepWarning(this.settingConflictWarning)}
|
||||
</StepHeader>
|
||||
<StepContent className={className}>
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
|
|
|
@ -4,7 +4,8 @@ import {
|
|||
StepWrapper,
|
||||
StepHeader,
|
||||
StepContent,
|
||||
StepHeaderProps
|
||||
StepHeaderProps,
|
||||
StepWarning
|
||||
} from "../index";
|
||||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
|
||||
|
@ -52,3 +53,11 @@ describe("<StepContent />", () => {
|
|||
expect(div.hasClass("step-class")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("<StepWarning />", () => {
|
||||
it("renders", () => {
|
||||
const wrapper = mount(StepWarning("warning"));
|
||||
expect(wrapper.find("i").hasClass("fa-exclamation-triangle")).toBeTruthy();
|
||||
expect(wrapper.text()).toContain("Hardware setting conflict.");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./step_wrapper";
|
||||
export * from "./step_header";
|
||||
export * from "./step_content";
|
||||
export * from "./step_warning";
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import * as React from "react";
|
||||
import { t } from "i18next";
|
||||
import { Popover, Position, PopoverInteractionKind } from "@blueprintjs/core";
|
||||
|
||||
export function StepWarning(warning: string) {
|
||||
return <div className="step-warning">
|
||||
<Popover
|
||||
position={Position.LEFT_TOP}
|
||||
interactionKind={PopoverInteractionKind.HOVER}
|
||||
popoverClassName={"help"} >
|
||||
<i className="fa fa-exclamation-triangle" />
|
||||
<div>
|
||||
{warning}
|
||||
</div>
|
||||
</Popover>
|
||||
{t("Hardware setting conflict.")}
|
||||
</div>;
|
||||
}
|
Loading…
Reference in New Issue