Merge pull request #1439 from RickCarlino/interim_prod_branch
v8.0.5 - Iridescent Irispull/1443/head v8.0.5
commit
12eebc9f44
|
@ -6,15 +6,15 @@ module FarmEvents
|
|||
|
||||
has_executable_fields
|
||||
|
||||
BACKWARDS_END_TIME = "This event starts before it ends. Did you flip the "\
|
||||
BACKWARDS_END_TIME = "This event starts before it ends. Did you flip the " \
|
||||
"start and end times?"
|
||||
|
||||
BAD_START_TIME = "FarmEvent start time needs to be in the future, not" +
|
||||
" the past."
|
||||
BAD_START_TIME = "FarmEvent start time needs to be in the future, not" +
|
||||
" the past."
|
||||
required do
|
||||
model :device, class: Device
|
||||
model :device, class: Device
|
||||
integer :repeat, min: 1
|
||||
string :time_unit, in: FarmEvent::UNITS_OF_TIME
|
||||
string :time_unit, in: FarmEvent::UNITS_OF_TIME
|
||||
end
|
||||
|
||||
optional do
|
||||
|
@ -29,12 +29,14 @@ module FarmEvents
|
|||
end
|
||||
|
||||
def execute
|
||||
FarmEvent.transaction do
|
||||
p = inputs.merge(executable: executable)
|
||||
# Needs to be set this way for cleanup operations:
|
||||
p[:end_time] = (p[:start_time] + 1.minute) if is_one_time_event
|
||||
p.delete(:body)
|
||||
wrap_fragment_with(FarmEvent.create!(p))
|
||||
FarmEvent.auto_sync_debounce do
|
||||
FarmEvent.transaction do
|
||||
p = inputs.merge(executable: executable)
|
||||
# Needs to be set this way for cleanup operations:
|
||||
p[:end_time] = (p[:start_time] + 1.minute) if is_one_time_event
|
||||
p.delete(:body)
|
||||
wrap_fragment_with(FarmEvent.create!(p))
|
||||
end
|
||||
end
|
||||
rescue CeleryScript::TypeCheckError => q
|
||||
add_error :farm_event, :farm_event, q.message
|
||||
|
|
|
@ -12,18 +12,13 @@ import {
|
|||
readPing,
|
||||
markStale,
|
||||
markActive,
|
||||
isInactive,
|
||||
startPinging,
|
||||
ACTIVE_THRESHOLD,
|
||||
PING_INTERVAL
|
||||
} from "../ping_mqtt";
|
||||
import { Farmbot, RpcRequest, RpcRequestBodyItem } from "farmbot";
|
||||
import { dispatchNetworkDown, dispatchNetworkUp } from "../index";
|
||||
import { FarmBotInternalConfig } from "farmbot/dist/config";
|
||||
|
||||
const TOO_LATE_TIME_DIFF = ACTIVE_THRESHOLD + 1;
|
||||
const ACCEPTABLE_TIME_DIFF = ACTIVE_THRESHOLD - 1;
|
||||
|
||||
const state: Partial<FarmBotInternalConfig> = {
|
||||
LAST_PING_IN: 123,
|
||||
LAST_PING_OUT: 456
|
||||
|
@ -78,12 +73,6 @@ describe("ping util", () => {
|
|||
expectActive();
|
||||
});
|
||||
|
||||
it("checks if the bot isInactive()", () => {
|
||||
expect(isInactive(1, 1 + TOO_LATE_TIME_DIFF)).toBeTruthy();
|
||||
expect(isInactive(1, 1)).toBeFalsy();
|
||||
expect(isInactive(1, 1 + ACCEPTABLE_TIME_DIFF)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("binds event handlers with startPinging()", (done) => {
|
||||
const bot = fakeBot();
|
||||
startPinging(bot);
|
||||
|
|
|
@ -47,9 +47,9 @@ describe("connectivity reducer", () => {
|
|||
});
|
||||
|
||||
it("broadcasts PING_NO", () => {
|
||||
pingNO("yep");
|
||||
pingNO("yep", 123);
|
||||
expect(store.dispatch).toHaveBeenCalledWith({
|
||||
payload: { id: "yep", },
|
||||
payload: { id: "yep", at: 123 },
|
||||
type: "PING_NO"
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,7 +51,7 @@ export const pingOK = (id: string, at: number) => {
|
|||
store.dispatch(action);
|
||||
};
|
||||
|
||||
export const pingNO = (id: string) => {
|
||||
const action = { type: Actions.PING_NO, payload: { id } };
|
||||
export const pingNO = (id: string, at: number) => {
|
||||
const action = { type: Actions.PING_NO, payload: { id, at } };
|
||||
store.dispatch(action);
|
||||
};
|
||||
|
|
|
@ -12,8 +12,7 @@ import { API } from "../api/index";
|
|||
import { FarmBotInternalConfig } from "farmbot/dist/config";
|
||||
import { now } from "../devices/connectivity/qos";
|
||||
|
||||
export const PING_INTERVAL = 1800;
|
||||
export const ACTIVE_THRESHOLD = PING_INTERVAL * 3;
|
||||
export const PING_INTERVAL = 2000;
|
||||
|
||||
export const LAST_IN: keyof FarmBotInternalConfig = "LAST_PING_IN";
|
||||
export const LAST_OUT: keyof FarmBotInternalConfig = "LAST_PING_OUT";
|
||||
|
@ -33,14 +32,10 @@ export function markActive() {
|
|||
dispatchNetworkUp("bot.mqtt", now());
|
||||
}
|
||||
|
||||
export function isInactive(last: number, now_: number): boolean {
|
||||
return last ? (now_ - last) > ACTIVE_THRESHOLD : true;
|
||||
}
|
||||
|
||||
export function sendOutboundPing(bot: Farmbot) {
|
||||
const id = uuid();
|
||||
const ok = () => pingOK(id, now()); markActive();
|
||||
const no = () => pingNO(id); markStale();
|
||||
const no = () => pingNO(id, now()); markStale();
|
||||
dispatchQosStart(id);
|
||||
bot.ping().then(ok, no);
|
||||
}
|
||||
|
|
|
@ -14,8 +14,9 @@ export const DEFAULT_STATE: ConnectionState = {
|
|||
"user.api": undefined
|
||||
},
|
||||
pings: {
|
||||
}
|
||||
},
|
||||
};
|
||||
type PingResultPayload = { id: string, at: number };
|
||||
|
||||
export let connectivityReducer =
|
||||
generateReducer<ConnectionState>(DEFAULT_STATE)
|
||||
|
@ -25,12 +26,12 @@ export let connectivityReducer =
|
|||
pings: startPing(s.pings, payload.id)
|
||||
};
|
||||
})
|
||||
.add<{ id: string, at: number }>(Actions.PING_OK, (s, { payload }) => {
|
||||
.add<PingResultPayload>(Actions.PING_OK, (s, { payload }) => {
|
||||
s.pings = completePing(s.pings, payload.id, payload.at);
|
||||
|
||||
return s;
|
||||
})
|
||||
.add<{ id: string }>(Actions.PING_NO, (s, { payload }) => {
|
||||
.add<PingResultPayload>(Actions.PING_NO, (s, { payload }) => {
|
||||
s.pings = failPing(s.pings, payload.id);
|
||||
|
||||
return s;
|
||||
|
|
|
@ -147,7 +147,7 @@ describe("sync()", function () {
|
|||
describe("execSequence()", function () {
|
||||
it("calls execSequence", async () => {
|
||||
await actions.execSequence(1);
|
||||
expect(mockDevice.execSequence).toHaveBeenCalledWith(1);
|
||||
expect(mockDevice.execSequence).toHaveBeenCalledWith(1, undefined);
|
||||
expect(success).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
|
@ -151,9 +151,15 @@ export function execSequence(
|
|||
const noun = t("Sequence execution");
|
||||
if (sequenceId) {
|
||||
commandOK(noun)();
|
||||
return bodyVariables
|
||||
? getDevice().execSequence(sequenceId, bodyVariables).catch(commandErr(noun))
|
||||
: getDevice().execSequence(sequenceId).catch(commandErr(noun));
|
||||
return getDevice()
|
||||
.execSequence(sequenceId, bodyVariables)
|
||||
.catch((x: Error) => {
|
||||
if (x && (typeof x == "object") && (typeof x.message == "string")) {
|
||||
error(x.message);
|
||||
} else {
|
||||
commandErr(noun);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error(t("Can't execute unsaved sequences"));
|
||||
}
|
||||
|
|
|
@ -104,9 +104,9 @@ export const calculateLatency =
|
|||
total: latency.length
|
||||
};
|
||||
|
||||
/** SIDE EFFECT WARNING: We do analytics on every 100th ping to gauge
|
||||
/** SIDE EFFECT WARNING: We do analytics on every nth ping to gauge
|
||||
* overall system health. This is the least invasive place to put it. */
|
||||
const doReport = !!report.total && !(report.total % 10);
|
||||
const doReport = !!report.total && !(report.total % 100);
|
||||
doReport && window.logStore.log("FBOS Ping QoS Message", report, "info");
|
||||
return report;
|
||||
};
|
||||
|
|
|
@ -12,26 +12,95 @@ jest.mock("../../../farmware/weed_detector/actions", () => ({
|
|||
|
||||
import * as React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import { CreatePoints, CreatePointsProps } from "../create_points";
|
||||
import {
|
||||
CreatePoints,
|
||||
CreatePointsProps,
|
||||
mapStateToProps
|
||||
} from "../create_points";
|
||||
import { initSave } from "../../../api/crud";
|
||||
import { deletePoints } from "../../../farmware/weed_detector/actions";
|
||||
import { Actions } from "../../../constants";
|
||||
import { clickButton } from "../../../__test_support__/helpers";
|
||||
import { fakeState } from "../../../__test_support__/fake_state";
|
||||
import { DeepPartial } from "redux";
|
||||
import { CurrentPointPayl } from "../../interfaces";
|
||||
|
||||
const FAKE_POINT: CurrentPointPayl =
|
||||
({ name: "My Point", cx: 13, cy: 22, r: 345, color: "red" });
|
||||
|
||||
describe("mapStateToProps", () => {
|
||||
it("maps state to props", () => {
|
||||
const state = fakeState();
|
||||
state
|
||||
.resources
|
||||
.consumers
|
||||
.farm_designer
|
||||
.currentPoint = FAKE_POINT;
|
||||
const result = mapStateToProps(state);
|
||||
const { currentPoint } = result;
|
||||
expect(currentPoint).toBeTruthy();
|
||||
if (currentPoint) {
|
||||
expect(currentPoint.cx).toEqual(13);
|
||||
expect(currentPoint.cy).toEqual(22);
|
||||
} else {
|
||||
fail("Nope");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("<CreatePoints />", () => {
|
||||
const fakeProps = (): CreatePointsProps => {
|
||||
return {
|
||||
dispatch: jest.fn(),
|
||||
currentPoint: undefined
|
||||
currentPoint: undefined,
|
||||
deviceY: 1.23,
|
||||
deviceX: 3.21
|
||||
};
|
||||
};
|
||||
|
||||
const fakeInstance = () => {
|
||||
const props = fakeProps();
|
||||
props.currentPoint = FAKE_POINT;
|
||||
return new CreatePoints(props);
|
||||
};
|
||||
|
||||
it("renders", () => {
|
||||
const wrapper = mount(<CreatePoints {...fakeProps()} />);
|
||||
["create point", "cancel", "delete", "x", "y", "radius", "color"]
|
||||
.map(string => expect(wrapper.text().toLowerCase()).toContain(string));
|
||||
});
|
||||
|
||||
it("updates specific fields", () => {
|
||||
const i = fakeInstance();
|
||||
const updater = i.updateValue("color");
|
||||
type Event = React.SyntheticEvent<HTMLInputElement>;
|
||||
const e: DeepPartial<Event> = {
|
||||
currentTarget: {
|
||||
value: "cheerful hue"
|
||||
}
|
||||
};
|
||||
updater(e as Event);
|
||||
expect(i.props.currentPoint).toBeTruthy();
|
||||
expect(i.props.dispatch).toHaveBeenCalledWith({
|
||||
payload: {
|
||||
color: "cheerful hue",
|
||||
cx: 13,
|
||||
cy: 22,
|
||||
name: "My Point",
|
||||
r: 345,
|
||||
},
|
||||
type: "SET_CURRENT_POINT_DATA",
|
||||
});
|
||||
});
|
||||
|
||||
it("updates current point", () => {
|
||||
const p = fakeInstance();
|
||||
p.updateCurrentPoint();
|
||||
expect(p.props.dispatch).toHaveBeenCalledWith({
|
||||
type: "SET_CURRENT_POINT_DATA",
|
||||
payload: { cx: 13, cy: 22, name: "My Point", r: 345, color: "red" },
|
||||
});
|
||||
});
|
||||
it("creates point", () => {
|
||||
const wrapper = mount(<CreatePoints {...fakeProps()} />);
|
||||
wrapper.setState({ cx: 10, cy: 20, r: 30, color: "red" });
|
||||
|
@ -93,17 +162,14 @@ describe("<CreatePoints />", () => {
|
|||
const p = fakeProps();
|
||||
p.currentPoint = { cx: 1, cy: 2, r: 3, color: "blue" };
|
||||
const wrapper = shallow<CreatePoints>(<CreatePoints {...p} />);
|
||||
wrapper.instance().getPointData();
|
||||
expect(wrapper.instance().state).toEqual({
|
||||
color: "blue", cx: 1, cy: 2, r: 3
|
||||
});
|
||||
});
|
||||
|
||||
it("fills the state with default data", () => {
|
||||
const wrapper = shallow<CreatePoints>(<CreatePoints {...fakeProps()} />);
|
||||
wrapper.instance().getPointData();
|
||||
expect(wrapper.instance().state).toEqual({
|
||||
color: "green", cx: 0, cy: 0, r: 1, name: "Created Point"
|
||||
const i = wrapper.instance();
|
||||
expect(i.state).toEqual({});
|
||||
expect(i.getPointData()).toEqual({
|
||||
name: undefined,
|
||||
cx: 1,
|
||||
cy: 2,
|
||||
r: 3,
|
||||
color: "blue"
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -6,12 +6,19 @@ jest.mock("../../../history", () => ({
|
|||
history: { push: jest.fn() }
|
||||
}));
|
||||
|
||||
const mockMoveAbs = jest.fn();
|
||||
|
||||
jest.mock("../../../device", () => {
|
||||
return { getDevice: () => ({ moveAbsolute: mockMoveAbs }) };
|
||||
});
|
||||
|
||||
import * as React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import { EditPoint, EditPointProps, mapStateToProps } from "../point_info";
|
||||
import { EditPoint, EditPointProps, mapStateToProps, moveToPoint } from "../point_info";
|
||||
import { fakePoint } from "../../../__test_support__/fake_state/resources";
|
||||
import { fakeState } from "../../../__test_support__/fake_state";
|
||||
import { buildResourceIndex } from "../../../__test_support__/resource_index_builder";
|
||||
import { getDevice } from "../../../device";
|
||||
|
||||
describe("<EditPoint />", () => {
|
||||
const fakeProps = (): EditPointProps => ({
|
||||
|
@ -42,3 +49,12 @@ describe("mapStateToProps()", () => {
|
|||
expect(props.findPoint(1)).toEqual(point);
|
||||
});
|
||||
});
|
||||
|
||||
describe("moveToPoint()", () => {
|
||||
it("moves the device to a particular point", () => {
|
||||
const coords = { x: 1, y: -2, z: 3 };
|
||||
const mover = moveToPoint(coords);
|
||||
mover();
|
||||
expect(getDevice().moveAbsolute).toHaveBeenCalledWith(coords);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,40 +1,54 @@
|
|||
import * as React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Everything, ResourceColor } from "../../interfaces";
|
||||
import {
|
||||
Everything,
|
||||
ResourceColor
|
||||
} from "../../interfaces";
|
||||
import { initSave } from "../../api/crud";
|
||||
import {
|
||||
Row, Col, BlurableInput, ColorPicker
|
||||
Row,
|
||||
Col,
|
||||
BlurableInput,
|
||||
ColorPicker
|
||||
} from "../../ui/index";
|
||||
import { CurrentPointPayl } from "../interfaces";
|
||||
import { Actions, Content } from "../../constants";
|
||||
import { deletePoints } from "../../farmware/weed_detector/actions";
|
||||
import { clone } from "lodash";
|
||||
import { GenericPointer } from "farmbot/dist/resources/api_resources";
|
||||
import {
|
||||
DesignerPanel, DesignerPanelHeader, DesignerPanelContent
|
||||
DesignerPanel,
|
||||
DesignerPanelHeader,
|
||||
DesignerPanelContent
|
||||
} from "./designer_panel";
|
||||
import { parseIntInput } from "../../util";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
|
||||
export function mapStateToProps(props: Everything) {
|
||||
export function mapStateToProps(props: Everything): CreatePointsProps {
|
||||
const { position } = props.bot.hardware.location_data;
|
||||
return {
|
||||
dispatch: props.dispatch,
|
||||
currentPoint: props.resources.consumers.farm_designer.currentPoint
|
||||
currentPoint: props.resources.consumers.farm_designer.currentPoint,
|
||||
deviceX: position.x || 0,
|
||||
deviceY: position.y || 0,
|
||||
};
|
||||
}
|
||||
|
||||
export interface CreatePointsProps {
|
||||
dispatch: Function;
|
||||
currentPoint: CurrentPointPayl | undefined;
|
||||
deviceX: number;
|
||||
deviceY: number;
|
||||
}
|
||||
|
||||
interface CreatePointsState {
|
||||
name: string;
|
||||
cx: number;
|
||||
cy: number;
|
||||
r: number;
|
||||
color: string;
|
||||
}
|
||||
type CreatePointsState = Partial<CurrentPointPayl>;
|
||||
|
||||
const DEFAULTS: CurrentPointPayl = {
|
||||
name: "Created Point",
|
||||
cx: 1,
|
||||
cy: 1,
|
||||
r: 15,
|
||||
color: "red"
|
||||
};
|
||||
|
||||
@connect(mapStateToProps)
|
||||
export class CreatePoints
|
||||
|
@ -44,19 +58,26 @@ export class CreatePoints
|
|||
this.state = {};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
this.getPointData();
|
||||
}
|
||||
attr = <T extends (keyof CurrentPointPayl & keyof CreatePointsState)>(key: T,
|
||||
fallback = DEFAULTS[key]): CurrentPointPayl[T] => {
|
||||
const p = this.props.currentPoint;
|
||||
const userValue = this.state[key] as CurrentPointPayl[T] | undefined;
|
||||
const propValue = p ? p[key] : fallback;
|
||||
if (typeof userValue === "undefined") {
|
||||
return propValue;
|
||||
} else {
|
||||
return userValue;
|
||||
}
|
||||
};
|
||||
|
||||
getPointData = () => {
|
||||
const point = this.props.currentPoint;
|
||||
this.setState({
|
||||
name: point ? point.name : "Created Point",
|
||||
cx: point ? point.cx : 0,
|
||||
cy: point ? point.cy : 0,
|
||||
r: point ? point.r : 1,
|
||||
color: point ? point.color : "green"
|
||||
});
|
||||
getPointData = (): CurrentPointPayl => {
|
||||
return {
|
||||
name: this.attr("name"),
|
||||
cx: this.attr("cx"),
|
||||
cy: this.attr("cy"),
|
||||
r: this.attr("r"),
|
||||
color: this.attr("color"),
|
||||
};
|
||||
}
|
||||
|
||||
cancel = () => {
|
||||
|
@ -65,7 +86,10 @@ export class CreatePoints
|
|||
payload: undefined
|
||||
});
|
||||
this.setState({
|
||||
cx: undefined, cy: undefined, r: undefined, color: undefined
|
||||
cx: undefined,
|
||||
cy: undefined,
|
||||
r: undefined,
|
||||
color: undefined
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -76,7 +100,7 @@ export class CreatePoints
|
|||
updateCurrentPoint = () => {
|
||||
this.props.dispatch({
|
||||
type: Actions.SET_CURRENT_POINT_DATA,
|
||||
payload: this.state
|
||||
payload: this.getPointData()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -86,7 +110,7 @@ export class CreatePoints
|
|||
const { value } = e.currentTarget;
|
||||
this.setState({ [key]: value });
|
||||
if (this.props.currentPoint) {
|
||||
const point = clone(this.props.currentPoint);
|
||||
const point = this.getPointData();
|
||||
switch (key) {
|
||||
case "name":
|
||||
case "color":
|
||||
|
@ -105,26 +129,24 @@ export class CreatePoints
|
|||
|
||||
changeColor = (color: ResourceColor) => {
|
||||
this.setState({ color });
|
||||
if (this.props.currentPoint) {
|
||||
const { cx, cy, r, name } = this.props.currentPoint;
|
||||
this.props.dispatch({
|
||||
type: Actions.SET_CURRENT_POINT_DATA,
|
||||
payload: { cx, cy, r, color, name }
|
||||
});
|
||||
}
|
||||
this.forceUpdate();
|
||||
this.props.dispatch({
|
||||
type: Actions.SET_CURRENT_POINT_DATA,
|
||||
payload: this.getPointData()
|
||||
});
|
||||
}
|
||||
|
||||
createPoint = () => {
|
||||
const { cx, cy, r, color, name } = this.state;
|
||||
const body: GenericPointer = {
|
||||
pointer_type: "GenericPointer",
|
||||
name: name || "Created Point",
|
||||
meta: { color, created_by: "farm-designer" },
|
||||
x: cx || 0,
|
||||
y: cy || 0,
|
||||
name: this.attr("name") || "Created Point",
|
||||
meta: {
|
||||
color: this.attr("color"),
|
||||
created_by: "farm-designer"
|
||||
},
|
||||
x: this.attr("cx"),
|
||||
y: this.attr("cy"),
|
||||
z: 0,
|
||||
radius: r || 1,
|
||||
radius: this.attr("r"),
|
||||
};
|
||||
this.props.dispatch(initSave("Point", body));
|
||||
this.cancel();
|
||||
|
@ -138,12 +160,11 @@ export class CreatePoints
|
|||
name="name"
|
||||
type="text"
|
||||
onCommit={this.updateValue("name")}
|
||||
value={this.state.name || "Created Point"} />
|
||||
value={this.attr("name") || ""} />
|
||||
</Col>
|
||||
</Row>;
|
||||
|
||||
PointProperties = () => {
|
||||
const { cx, cy, r, color } = this.state;
|
||||
return <Row>
|
||||
<Col xs={3}>
|
||||
<label>{t("X")}</label>
|
||||
|
@ -151,7 +172,7 @@ export class CreatePoints
|
|||
name="cx"
|
||||
type="number"
|
||||
onCommit={this.updateValue("cx")}
|
||||
value={cx || 0} />
|
||||
value={this.attr("cx", this.props.deviceX)} />
|
||||
</Col>
|
||||
<Col xs={3}>
|
||||
<label>{t("Y")}</label>
|
||||
|
@ -159,7 +180,7 @@ export class CreatePoints
|
|||
name="cy"
|
||||
type="number"
|
||||
onCommit={this.updateValue("cy")}
|
||||
value={cy || 0} />
|
||||
value={this.attr("cy", this.props.deviceY)} />
|
||||
</Col>
|
||||
<Col xs={3}>
|
||||
<label>{t("radius")}</label>
|
||||
|
@ -167,13 +188,13 @@ export class CreatePoints
|
|||
name="r"
|
||||
type="number"
|
||||
onCommit={this.updateValue("r")}
|
||||
value={r || 0}
|
||||
value={this.attr("r", DEFAULTS.r)}
|
||||
min={0} />
|
||||
</Col>
|
||||
<Col xs={3}>
|
||||
<label>{t("color")}</label>
|
||||
<ColorPicker
|
||||
current={color as ResourceColor || "green"}
|
||||
current={this.attr("color") as ResourceColor}
|
||||
onChange={this.changeColor} />
|
||||
</Col>
|
||||
</Row>;
|
||||
|
|
|
@ -6,8 +6,13 @@ import {
|
|||
import { t } from "../../i18next_wrapper";
|
||||
import { history, getPathArray } from "../../history";
|
||||
import { Everything } from "../../interfaces";
|
||||
import { TaggedPoint } from "farmbot";
|
||||
import { TaggedPoint, Vector3 } from "farmbot";
|
||||
import { maybeFindPointById } from "../../resources/selectors";
|
||||
import { DeleteButton } from "../../controls/pin_form_fields";
|
||||
import { getDevice } from "../../device";
|
||||
|
||||
export const moveToPoint =
|
||||
(body: Vector3) => () => getDevice().moveAbsolute(body);
|
||||
|
||||
export interface EditPointProps {
|
||||
dispatch: Function;
|
||||
|
@ -33,6 +38,34 @@ export class EditPoint extends React.Component<EditPointProps, {}> {
|
|||
return <span>{t("Redirecting...")}</span>;
|
||||
}
|
||||
|
||||
temporaryMenu = (p: TaggedPoint) => {
|
||||
const { body } = p;
|
||||
return <div>
|
||||
<h3>
|
||||
Point {body.name || body.id || ""} @ ({body.x}, {body.y}, {body.z})
|
||||
</h3>
|
||||
<ul>
|
||||
{
|
||||
Object.entries(body.meta).map(([k, v]) => {
|
||||
return <li>{k}: {v}</li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
<button
|
||||
className="green fb-button"
|
||||
type="button"
|
||||
onClick={moveToPoint(body)}>
|
||||
{t("Move Device to Point")}
|
||||
</button>
|
||||
<DeleteButton
|
||||
dispatch={this.props.dispatch}
|
||||
uuid={p.uuid}
|
||||
onDestroy={this.fallback}>
|
||||
{t("Delete Point")}
|
||||
</DeleteButton>
|
||||
</div>;
|
||||
};
|
||||
|
||||
default = (point: TaggedPoint) => {
|
||||
return <DesignerPanel panelName={"plant-info"} panelColor={"green"}>
|
||||
<DesignerPanelHeader
|
||||
|
@ -42,6 +75,7 @@ export class EditPoint extends React.Component<EditPointProps, {}> {
|
|||
backTo={"/app/designer/points"}>
|
||||
</DesignerPanelHeader>
|
||||
<DesignerPanelContent panelName={"plants"}>
|
||||
{this.point && this.temporaryMenu(this.point)}
|
||||
</DesignerPanelContent>
|
||||
</DesignerPanel>;
|
||||
}
|
||||
|
|
|
@ -90,7 +90,8 @@ describe("<TestButton/>", () => {
|
|||
btn.simulate("click");
|
||||
expect(btn.hasClass("orange")).toBeTruthy();
|
||||
expect(warning).not.toHaveBeenCalled();
|
||||
expect(mockDevice.execSequence).toHaveBeenCalledWith(props.sequence.body.id);
|
||||
expect(mockDevice.execSequence)
|
||||
.toHaveBeenCalledWith(props.sequence.body.id, undefined);
|
||||
});
|
||||
|
||||
it("opens parameter assignment menu", () => {
|
||||
|
|
Loading…
Reference in New Issue