virtual farmbot encoder data

pull/508/head
gabrielburnworth 2017-10-19 23:26:41 -07:00
parent 1a726295a4
commit 5212cfb2a5
13 changed files with 120 additions and 52 deletions

View File

@ -84,7 +84,7 @@ export type Xyz = "x" | "y" | "z";
export type Axis = Xyz | "all";
export type BotPosition = Record<Xyz, (number | undefined)>;
export type BotLocationData = Record<LocationName, BotPosition> | undefined;
export type BotLocationData = Record<LocationName, BotPosition>;
export type StepsPerMmXY = Record<"x" | "y", (number | undefined)>;

View File

@ -25,7 +25,11 @@ describe("<FarmDesigner/>", () => {
plants: [],
toolSlots: [],
crops: [],
botPosition: { x: undefined, y: undefined, z: undefined },
botLocationData: {
position: { x: undefined, y: undefined, z: undefined },
scaled_encoders: { x: undefined, y: undefined, z: undefined },
raw_encoders: { x: undefined, y: undefined, z: undefined },
},
botMcuParams: bot.hardware.mcu_params,
stepsPerMmXY: { x: undefined, y: undefined }
};

View File

@ -170,7 +170,7 @@ export class FarmDesigner extends React.Component<Props, Partial<State>> {
plants={this.props.plants}
points={this.props.points}
toolSlots={this.props.toolSlots}
botPosition={this.props.botPosition}
botLocationData={this.props.botLocationData}
botSize={botSize}
stopAtHome={stopAtHome}
hoveredPlant={this.props.hoveredPlant}

View File

@ -11,7 +11,7 @@ import {
} from "../resources/tagged_resources";
import { PlantPointer } from "../interfaces";
import { SlotWithTool } from "../resources/interfaces";
import { BotPosition, StepsPerMmXY } from "../devices/interfaces";
import { BotPosition, StepsPerMmXY, BotLocationData } from "../devices/interfaces";
import { isNumber } from "lodash";
import { McuParams } from "farmbot/dist";
import { AxisNumberProperty, BotSize } from "./map/interfaces";
@ -46,7 +46,7 @@ export interface Props {
plants: TaggedPlantPointer[];
toolSlots: SlotWithTool[];
crops: TaggedCrop[];
botPosition: BotPosition;
botLocationData: BotLocationData;
botMcuParams: McuParams;
stepsPerMmXY: StepsPerMmXY;
}
@ -165,7 +165,7 @@ export interface GardenMapProps {
selectedPlant: TaggedPlantPointer | undefined;
hoveredPlant: TaggedPlantPointer | undefined;
crops: TaggedCrop[];
botPosition: BotPosition;
botLocationData: BotLocationData;
botSize: BotSize;
stopAtHome: Record<"x" | "y", boolean>;
zoomLvl: number;

View File

@ -38,7 +38,11 @@ describe("<GardenPlant/>", () => {
plants: [],
points: [],
toolSlots: [],
botPosition: { x: 0, y: 0, z: 0 },
botLocationData: {
position: { x: 0, y: 0, z: 0 },
scaled_encoders: { x: undefined, y: undefined, z: undefined },
raw_encoders: { x: undefined, y: undefined, z: undefined },
},
botSize: {
x: { value: 3000, isDefault: true },
y: { value: 1500, isDefault: true }

View File

@ -21,7 +21,11 @@ import { Dictionary } from "farmbot";
describe("<VirtualFarmBot/>", () => {
function fakeProps(): VirtualFarmBotProps {
return {
botPosition: { x: 0, y: 0, z: 0 },
botLocationData: {
position: { x: 0, y: 0, z: 0 },
scaled_encoders: { x: undefined, y: undefined, z: undefined },
raw_encoders: { x: undefined, y: undefined, z: undefined },
},
mapTransformProps: {
quadrant: 1, gridSize: { x: 3000, y: 1500 }
},
@ -70,9 +74,11 @@ describe("<VirtualFarmBot/>", () => {
it("changes location", () => {
const p = fakeProps();
p.mapTransformProps.quadrant = 2;
p.botPosition = { x: 100, y: 200, z: 0 };
p.botLocationData.position = { x: 100, y: 200, z: 0 };
const result = shallow(<VirtualFarmBot {...p } />);
expect(result.find("#gantry").props().x).toEqual(90);
const gantry = result.find("#gantry");
expect(gantry.length).toEqual(1);
expect(gantry.props().x).toEqual(90);
const UTM = result.find("circle").props();
expect(UTM.cx).toEqual(100);
expect(UTM.cy).toEqual(200);
@ -102,4 +108,23 @@ describe("<VirtualFarmBot/>", () => {
x1: 0, x2: 4, y1: 0, y2: 4
});
});
it("shows encoder position", () => {
mockStorj["encoderFigure"] = true;
const p = fakeProps();
p.mapTransformProps.quadrant = 2;
p.botLocationData.position = { x: 100, y: 200, z: 0 };
p.botLocationData.scaled_encoders = { x: 300, y: 400, z: 0 };
const wrapper = shallow(<VirtualFarmBot {...p } />);
expect(wrapper.find("#gantry").first().props().x).toEqual(90);
expect(wrapper.find("#gantry").last().props().x).toEqual(290);
const motorsUTM = wrapper.find("circle").first().props();
expect(motorsUTM.cx).toEqual(100);
expect(motorsUTM.cy).toEqual(200);
expect(motorsUTM.fillOpacity).toEqual(0.75);
const encodersUTM = wrapper.find("circle").last().props();
expect(encodersUTM.cx).toEqual(300);
expect(encodersUTM.cy).toEqual(400);
expect(encodersUTM.fillOpacity).toEqual(0.25);
});
});

View File

@ -282,7 +282,7 @@ export class GardenMap extends
<FarmBotLayer
mapTransformProps={mapTransformProps}
visible={!!this.props.showFarmbot}
botPosition={this.props.botPosition}
botLocationData={this.props.botLocationData}
stopAtHome={this.props.stopAtHome}
botSize={this.props.botSize}
plantAreaOffset={this.props.gridOffset} />
@ -304,8 +304,8 @@ export class GardenMap extends
plantAreaOffset={this.props.gridOffset} />
{this.state.selectionBox &&
<SelectionBox
selectionBox={this.state.selectionBox}
mapTransformProps={mapTransformProps} />}
selectionBox={this.state.selectionBox}
mapTransformProps={mapTransformProps} />}
</svg>
</svg>
</div>;

View File

@ -4,7 +4,7 @@ import {
TaggedGenericPointer
} from "../../resources/tagged_resources";
import { State, BotOriginQuadrant } from "../interfaces";
import { BotPosition } from "../../devices/interfaces";
import { BotPosition, BotLocationData } from "../../devices/interfaces";
export interface PlantLayerProps {
plants: TaggedPlantPointer[];
@ -92,7 +92,7 @@ export interface GridProps {
export interface VirtualFarmBotProps {
mapTransformProps: MapTransformProps;
botPosition: BotPosition;
botLocationData: BotLocationData;
plantAreaOffset: AxisNumberProperty;
}

View File

@ -7,7 +7,11 @@ describe("<FarmBotLayer/>", () => {
function fakeProps(): FarmBotLayerProps {
return {
visible: true,
botPosition: { x: 0, y: 0, z: 0 },
botLocationData: {
position: { x: undefined, y: undefined, z: undefined },
scaled_encoders: { x: undefined, y: undefined, z: undefined },
raw_encoders: { x: undefined, y: undefined, z: undefined },
},
mapTransformProps: {
quadrant: 1, gridSize: { x: 3000, y: 1500 }
},

View File

@ -10,7 +10,7 @@ export function FarmBotLayer(props: FarmBotLayerProps) {
return visible ? <g id="farmbot-layer">
<VirtualFarmBot
mapTransformProps={mapTransformProps}
botPosition={props.botPosition}
botLocationData={props.botLocationData}
plantAreaOffset={plantAreaOffset} />
<BotExtents
mapTransformProps={mapTransformProps}

View File

@ -1,9 +1,10 @@
import * as React from "react";
import { getXYFromQuadrant, getMapSize } from "./util";
import { VirtualFarmBotProps } from "./interfaces";
import { VirtualFarmBotProps, AxisNumberProperty } from "./interfaces";
import { Session } from "../../session";
import { BooleanSetting } from "../../session_keys";
import * as _ from "lodash";
import { BotOriginQuadrant } from "../interfaces";
type TrailRecord = Record<"x" | "y", number | undefined>;
@ -17,45 +18,70 @@ function getNewTrailArray(update: TrailRecord) {
return arr;
}
function botTrail(
array: TrailRecord[],
quadrant: BotOriginQuadrant,
gridSize: AxisNumberProperty) {
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} />;
}
})}
</g>;
}
function positionFigure(
name: string,
position: { qx: number, qy: number },
mapSize: AxisNumberProperty,
plantAreaOffset: AxisNumberProperty) {
const opacity = name.includes("encoder") ? 0.25 : 0.75;
return <g id={name}>
<rect id="gantry"
x={position.qx - 10}
y={-plantAreaOffset.y}
width={20}
height={mapSize.y}
fillOpacity={opacity}
fill={"#434343"} />
<circle id="UTM"
cx={position.qx}
cy={position.qy}
r={35}
fillOpacity={opacity}
fill={"#434343"} />
</g>;
}
export function VirtualFarmBot(props: VirtualFarmBotProps) {
const { x, y } = props.botPosition;
const { x, y } = props.botLocationData.position;
const { mapTransformProps, plantAreaOffset } = props;
const { quadrant, gridSize } = mapTransformProps;
const mapSize = getMapSize(gridSize, plantAreaOffset);
const { qx, qy } = getXYFromQuadrant((x || 0), (y || 0), quadrant, gridSize);
const positionQ = getXYFromQuadrant((x || 0), (y || 0), quadrant, gridSize);
const displayTrail = Session.getBool(BooleanSetting.displayTrail);
const update = { x, y };
const array = displayTrail ? getNewTrailArray(update) : [];
const encoderFigure = Session.getBool(BooleanSetting.encoderFigure);
const enc = props.botLocationData.scaled_encoders;
const encodersQ = getXYFromQuadrant(
(enc.x || 0), (enc.y || 0), quadrant, gridSize);
return <g id="virtual-farmbot">
<rect id="gantry"
x={qx - 10}
y={-plantAreaOffset.y}
width={20}
height={mapSize.y}
fillOpacity={0.75}
fill={"#434343"} />
<circle id="UTM"
cx={qx}
cy={qy}
r={35}
fillOpacity={0.75}
fill={"#434343"} />
{positionFigure("motor-position", positionQ, mapSize, plantAreaOffset)}
{encoderFigure &&
positionFigure("encoder-position", encodersQ, mapSize, plantAreaOffset)}
{displayTrail &&
<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} />;
}
})}
</g>}
botTrail(array, quadrant, gridSize)}
</g>;
}

View File

@ -5,7 +5,7 @@ import {
selectAllCrops,
joinToolsAndSlot
} from "../resources/selectors";
import { BotPosition, StepsPerMmXY } from "../devices/interfaces";
import { BotLocationData, StepsPerMmXY } from "../devices/interfaces";
import { isNumber } from "lodash";
export function mapStateToProps(props: Everything) {
@ -18,11 +18,15 @@ export function mapStateToProps(props: Everything) {
const { plantUUID } = props.resources.consumers.farm_designer.hoveredPlant;
const hoveredPlant = plants.filter(x => x.uuid === plantUUID)[0];
const getBotPosition = (): BotPosition => {
const getBotLocationData = (): BotLocationData => {
if (props.bot.hardware.location_data) {
return props.bot.hardware.location_data.position;
return props.bot.hardware.location_data;
}
return { x: undefined, y: undefined, z: undefined };
return {
position: { x: undefined, y: undefined, z: undefined },
scaled_encoders: { x: undefined, y: undefined, z: undefined },
raw_encoders: { x: undefined, y: undefined, z: undefined },
};
};
function stepsPerMmXY(): StepsPerMmXY {
@ -43,7 +47,7 @@ export function mapStateToProps(props: Everything) {
toolSlots: joinToolsAndSlot(props.resources.index),
hoveredPlant,
plants,
botPosition: getBotPosition(),
botLocationData: getBotLocationData(),
botMcuParams: props.bot.hardware.mcu_params,
stepsPerMmXY: stepsPerMmXY()
};

View File

@ -19,6 +19,7 @@ export enum BooleanSetting {
mapXL = "mapXL",
disableAnimations = "disableAnimations",
displayTrail = "displayTrail",
encoderFigure = "encoderFigure",
}
export enum NumericSetting {