import * as React from "react"; import { Xyz, LocationName, Dictionary } from "farmbot"; import moment from "moment"; import { BotLocationData, BotPosition } from "../../devices/interfaces"; import { trim } from "../../util"; import { cloneDeep, max, get, isNumber, isEqual, takeRight, ceil, range, } from "lodash"; import { t } from "../../i18next_wrapper"; const HEIGHT = 50; const HISTORY_LENGTH_SECONDS = 120; const BORDER_WIDTH = 15; const BORDERS = BORDER_WIDTH * 2; const MAX_X = HISTORY_LENGTH_SECONDS; const DEFAULT_Y_MAX = 100; const COLOR_LOOKUP: Dictionary = { x: "red", y: "green", z: "blue" }; const LINEWIDTH_LOOKUP: Dictionary = { position: 0.5, scaled_encoders: 0.25 }; export enum MotorPositionHistory { array = "motorPositionHistoryArray", } type Entry = { timestamp: number, locationData: Record }; type Paths = Record>; const getArray = (): Entry[] => JSON.parse(get(sessionStorage, MotorPositionHistory.array, "[]")); const getReversedArray = (): Entry[] => cloneDeep(getArray()).reverse(); const getLastEntry = (): Entry | undefined => { const array = getArray(); return array[array.length - 1]; }; const findYLimit = (): number => { const array = getArray(); const arrayAbsMax = max(array.map(entry => max(["position", "scaled_encoders"].map((key: LocationName) => max(["x", "y", "z"].map((axis: Xyz) => Math.abs(entry.locationData[key][axis] || 0) + 1)))))); return Math.max(ceil(arrayAbsMax || 0, -2), DEFAULT_Y_MAX); }; const updateArray = (update: Entry): Entry[] => { const arr = getArray(); const last = getLastEntry(); if (update && isNumber(update.locationData.position.x) && (!last || !isEqual(last.timestamp, update.timestamp))) { arr.push(update); } const newArray = takeRight(arr, 100) .filter(x => { const entryAge = (last ? last.timestamp : moment().unix()) - x.timestamp; return entryAge <= HISTORY_LENGTH_SECONDS; }); sessionStorage.setItem(MotorPositionHistory.array, JSON.stringify(newArray)); return newArray; }; const newPaths = (): Paths => ({ position: { x: "", y: "", z: "" }, scaled_encoders: { x: "", y: "", z: "" }, raw_encoders: { x: "", y: "", z: "" } }); const getPaths = (): Paths => { const last = getLastEntry(); const maxY = findYLimit(); const paths = newPaths(); if (last) { getReversedArray().map(entry => { ["position", "scaled_encoders"].map((key: LocationName) => { ["x", "y", "z"].map((axis: Xyz) => { const lastPos = last.locationData[key][axis]; const pos = entry.locationData[key][axis]; if (isNumber(lastPos) && isFinite(lastPos) && isNumber(maxY) && isNumber(pos)) { if (!paths[key][axis].startsWith("M")) { const yStart = -lastPos / maxY * HEIGHT / 2; paths[key][axis] = `M ${MAX_X},${yStart} `; } const x = MAX_X - (last.timestamp - entry.timestamp); const y = -pos / maxY * HEIGHT / 2; paths[key][axis] += `L ${x},${y} `; } }); }); }); } return paths; }; const TitleLegend = () => { const titleY = -(HEIGHT + BORDER_WIDTH) / 2; const legendX = HISTORY_LENGTH_SECONDS / 4; return {"X"} {"Y"} {"Z"} {t("Position (mm)")} ; }; const YAxisLabels = () => { const maxY = findYLimit(); return {[maxY, maxY / 2, 0, -maxY / 2, -maxY].map(yPosition => {yPosition} {yPosition} )} ; }; const XAxisLabels = () => {t("seconds ago")} {range(0, HISTORY_LENGTH_SECONDS + 1, 20).map(secondsAgo => {secondsAgo} )} ; const PlotBackground = () => ; const PlotLines = ({ locationData }: { locationData: BotLocationData }) => { updateArray({ timestamp: moment().unix(), locationData }); const paths = getPaths(); return {["position", "scaled_encoders"].map((key: LocationName) => ["x", "y", "z"].map((axis: Xyz) => ))} ; }; export const MotorPositionPlot = (props: { locationData: BotLocationData }) => { return ; };