add virtual bot trail setting
parent
8b6bf0d516
commit
fedd40f22d
|
@ -3,7 +3,7 @@ import { fetchLabFeatures } from "../labs_features_list_data";
|
|||
describe("fetchLabFeatures", () => {
|
||||
it("basically just initializes stuff", () => {
|
||||
const val = fetchLabFeatures();
|
||||
expect(val.length).toBe(8);
|
||||
expect(val.length).toBe(9);
|
||||
expect(val[0].value).toBeFalsy();
|
||||
const { callback } = val[0];
|
||||
if (callback) {
|
||||
|
|
|
@ -74,6 +74,13 @@ export const fetchLabFeatures = (): LabsFeature[] => ([
|
|||
value: false,
|
||||
confirmationMessage: t(Content.DISCARD_UNSAVED_CHANGES_CONFIRM)
|
||||
},
|
||||
{
|
||||
name: t("Display virtual FarmBot trail"),
|
||||
description: t(Content.VIRTUAL_TRAIL),
|
||||
storageKey: BooleanSetting.display_trail,
|
||||
value: false,
|
||||
callback: () => sessionStorage.virtualTrailRecords = "[]"
|
||||
},
|
||||
].map(fetchRealValue));
|
||||
|
||||
/** Always allow toggling from true => false (deactivate).
|
||||
|
|
|
@ -341,6 +341,11 @@ export namespace Content {
|
|||
trim(`Warning! When enabled, any unsaved changes
|
||||
will be discarded when refreshing or closing the page. Are you sure?`);
|
||||
|
||||
export const VIRTUAL_TRAIL =
|
||||
trim(`Display a virtual trail for FarmBot in the Farm Designer map to show
|
||||
movement and watering history while the map is open. Toggling this setting
|
||||
will clear data for the current trail.`);
|
||||
|
||||
// Device
|
||||
export const NOT_HTTPS =
|
||||
trim(`WARNING: Sending passwords via HTTP:// is not secure.`);
|
||||
|
|
|
@ -5,13 +5,17 @@ import { BotTrail, BotTrailProps, VirtualTrail } from "../bot_trail";
|
|||
describe("<BotTrail/>", () => {
|
||||
function fakeProps(): BotTrailProps {
|
||||
sessionStorage[VirtualTrail.records] = JSON.stringify([
|
||||
{ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 },
|
||||
{ x: 4, y: 4 }]);
|
||||
{ coord: { x: 0, y: 0 }, water: 0 },
|
||||
{ coord: { x: 1, y: 1 }, water: 10 },
|
||||
{ coord: { x: 2, y: 2 }, water: 0 },
|
||||
{ coord: { x: 3, y: 3 }, water: 0 },
|
||||
{ coord: { x: 4, y: 4 }, water: 20 }]);
|
||||
return {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
mapTransformProps: {
|
||||
quadrant: 2, gridSize: { x: 3000, y: 1500 }
|
||||
}
|
||||
},
|
||||
peripherals: []
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -19,28 +23,28 @@ describe("<BotTrail/>", () => {
|
|||
sessionStorage[VirtualTrail.length] = JSON.stringify(5);
|
||||
const p = fakeProps();
|
||||
p.mapTransformProps.quadrant = 2;
|
||||
const wrapper = shallow(<BotTrail {...p } />);
|
||||
const wrapper = shallow(<BotTrail {...p} />);
|
||||
const lines = wrapper.find("#trail").find("line");
|
||||
expect(lines.length).toEqual(4);
|
||||
expect(lines.first().props()).toEqual({
|
||||
id: "trail-line-1",
|
||||
stroke: "red",
|
||||
strokeOpacity: 0.25,
|
||||
strokeWidth: 0.5,
|
||||
strokeWidth: 1.5,
|
||||
x1: 2, x2: 1, y1: 2, y2: 1
|
||||
});
|
||||
expect(lines.last().props()).toEqual({
|
||||
id: "trail-line-4",
|
||||
stroke: "red",
|
||||
strokeOpacity: 1,
|
||||
strokeWidth: 2,
|
||||
strokeWidth: 3,
|
||||
x1: 0, x2: 4, y1: 0, y2: 4
|
||||
});
|
||||
});
|
||||
|
||||
it("shows default length trail", () => {
|
||||
sessionStorage[VirtualTrail.length] = undefined;
|
||||
const wrapper = shallow(<BotTrail {...fakeProps() } />);
|
||||
const wrapper = shallow(<BotTrail {...fakeProps()} />);
|
||||
const lines = wrapper.find("#trail").find("line");
|
||||
expect(lines.length).toEqual(5);
|
||||
});
|
||||
|
@ -49,9 +53,24 @@ describe("<BotTrail/>", () => {
|
|||
sessionStorage[VirtualTrail.length] = undefined;
|
||||
const p = fakeProps();
|
||||
p.position = { x: 4, y: 4, z: 0 };
|
||||
const wrapper = shallow(<BotTrail {...p } />);
|
||||
const wrapper = shallow(<BotTrail {...p} />);
|
||||
const lines = wrapper.find("#trail").find("line");
|
||||
expect(lines.length).toEqual(4);
|
||||
});
|
||||
|
||||
it("shows water", () => {
|
||||
const wrapper = shallow(<BotTrail {...fakeProps()} />);
|
||||
const circles = wrapper.find("#trail").find("circle");
|
||||
expect(circles.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("updates water circle size", () => {
|
||||
const p = fakeProps();
|
||||
p.position = { x: 4, y: 4, z: 0 };
|
||||
p.peripherals = [{ label: "water", value: true }];
|
||||
const wrapper = shallow(<BotTrail {...p} />);
|
||||
const water = wrapper.find("#trail").find("circle").last();
|
||||
expect(water.props().r).toEqual(21);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -3,20 +3,30 @@ import * as _ from "lodash";
|
|||
import { MapTransformProps } from "../interfaces";
|
||||
import { getXYFromQuadrant } from "../util";
|
||||
import { BotPosition } from "../../../devices/interfaces";
|
||||
import { Color } from "../../../ui";
|
||||
|
||||
type TrailRecord = Record<"x" | "y", number | undefined>;
|
||||
type TrailRecord = {
|
||||
coord: Record<"x" | "y", number | undefined>,
|
||||
water: number | undefined
|
||||
} | undefined;
|
||||
|
||||
export enum VirtualTrail {
|
||||
records = "virtualTrailRecords",
|
||||
length = "virtualTrailLength"
|
||||
}
|
||||
|
||||
function getNewTrailArray(update: TrailRecord): TrailRecord[] {
|
||||
function getNewTrailArray(update: TrailRecord, watering: boolean): TrailRecord[] {
|
||||
const key = VirtualTrail.records; // sessionStorage location
|
||||
const trailLength = _.get(sessionStorage, VirtualTrail.length, 100);
|
||||
const arr = JSON.parse(_.get(sessionStorage, key, "[]")); // get array
|
||||
const arr: TrailRecord[] = JSON.parse(_.get(sessionStorage, key, "[]"));
|
||||
if (arr.length > (trailLength - 1)) { arr.shift(); } // max length reached
|
||||
if (!_.isEqual(_.last(arr), update)) { arr.push(update); } // unique addition
|
||||
const last = arr[arr.length - 1]; // most recent item in array
|
||||
if (update && update.coord &&
|
||||
(!last || !_.isEqual(last.coord, update.coord))) { // coordinate comparison
|
||||
arr.push(update); // unique addition
|
||||
} else { // nothing new to add, increase water circle size if watering
|
||||
if (watering && last && _.isNumber(last.water)) { last.water += 1; }
|
||||
}
|
||||
sessionStorage.setItem(key, JSON.stringify(arr)); // save array
|
||||
return _.takeRight(arr, trailLength);
|
||||
}
|
||||
|
@ -24,23 +34,39 @@ function getNewTrailArray(update: TrailRecord): TrailRecord[] {
|
|||
export interface BotTrailProps {
|
||||
position: BotPosition;
|
||||
mapTransformProps: MapTransformProps;
|
||||
peripherals: { label: string, value: boolean }[];
|
||||
}
|
||||
|
||||
export function BotTrail(props: BotTrailProps) {
|
||||
const { quadrant, gridSize } = props.mapTransformProps;
|
||||
const toQ = (ox: number, oy: number) =>
|
||||
getXYFromQuadrant(ox, oy, quadrant, gridSize);
|
||||
|
||||
const { x, y } = props.position;
|
||||
const array = getNewTrailArray({ x, y });
|
||||
const watering = !!_.first(props.peripherals
|
||||
.filter(p => p.label.toLowerCase().includes("water"))
|
||||
.map(p => p.value));
|
||||
|
||||
const array = getNewTrailArray({ coord: { x, y }, water: 0 }, watering);
|
||||
|
||||
return <g id="trail">
|
||||
{array.map((cur: TrailRecord, i: number) => {
|
||||
const prev = array[i - 1]; // previous trail coordinate
|
||||
const opacity = Math.round((i / (array.length - 1)) * 100) / 100;
|
||||
if (i > 0 && _.isNumber(prev.x) && _.isNumber(prev.y)
|
||||
&& _.isNumber(cur.x) && _.isNumber(cur.y)) {
|
||||
const p1 = getXYFromQuadrant(cur.x, cur.y, quadrant, gridSize);
|
||||
const p2 = getXYFromQuadrant(prev.x, prev.y, quadrant, gridSize);
|
||||
return <line id={`trail-line-${i}`} key={i}
|
||||
stroke="red" strokeOpacity={opacity} strokeWidth={opacity * 2}
|
||||
x1={p1.qx} y1={p1.qy} x2={p2.qx} y2={p2.qy} />;
|
||||
const prev = (array[i - 1] || { coord: undefined }).coord; // prev coord
|
||||
const opacity = _.round(Math.max(0.25, i / (array.length - 1)), 2);
|
||||
if (i > 0 && cur && prev && _.isNumber(prev.x) && _.isNumber(prev.y)
|
||||
&& _.isNumber(cur.coord.x) && _.isNumber(cur.coord.y)
|
||||
&& _.isNumber(cur.water)) {
|
||||
const p1 = toQ(cur.coord.x, cur.coord.y);
|
||||
const p2 = toQ(prev.x, prev.y);
|
||||
return <g key={i}>
|
||||
<line id={`trail-line-${i}`}
|
||||
stroke="red" strokeOpacity={opacity} strokeWidth={1 + opacity * 2}
|
||||
x1={p1.qx} y1={p1.qy} x2={p2.qx} y2={p2.qy} />
|
||||
{cur.water &&
|
||||
<circle id={`trail-water-${i}`}
|
||||
fill={Color.blue} opacity={opacity / 2}
|
||||
cx={p1.qx} cy={p1.qy} r={cur.water} />}
|
||||
</g>;
|
||||
}
|
||||
})}
|
||||
</g>;
|
||||
|
|
|
@ -32,6 +32,7 @@ export function VirtualFarmBot(props: VirtualFarmBotProps) {
|
|||
{displayTrail &&
|
||||
<BotTrail
|
||||
position={props.botLocationData.position}
|
||||
mapTransformProps={mapTransformProps} />}
|
||||
mapTransformProps={mapTransformProps}
|
||||
peripherals={peripherals} />}
|
||||
</g>;
|
||||
}
|
||||
|
|
|
@ -14,4 +14,5 @@ export enum Color {
|
|||
soilCloud = "#90612f",
|
||||
black = "#000000",
|
||||
orange = "#ffa500",
|
||||
blue = "#3377dd",
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue