FarmBot/Farmbot-Web-App

View on GitHub
frontend/photos/weed_detector/index.tsx

Summary

Maintainability
C
1 day
Test Coverage
import React from "react";
import { WeedDetectorState, WeedDetectorProps } from "./interfaces";
import { scanImage, detectPlants } from "./actions";
import { deletePoints } from "../../api/delete_points";
import { Progress } from "../../util";
import { ImageWorkspace, NumericKeyName } from "../image_workspace";
import { WDENVKey } from "../remote_env/interfaces";
import {
  namespace, WD_KEY_DEFAULTS, WEED_DETECTOR_KEY_PART,
} from "../remote_env/constants";
import { envGet } from "../remote_env/selectors";
import { MustBeOnline, isBotOnline } from "../../devices/must_be_online";
import { t } from "../../i18next_wrapper";
import { cameraBtnProps } from "../capture_settings/camera_selection";
import { BoolConfig, NumberBoxConfig } from "../camera_calibration/config";
import { SPECIAL_VALUE_DDI } from "../camera_calibration/constants";
import { DeviceSetting, ToolTips } from "../../constants";
import { formatEnvKey } from "../remote_env/translators";
import { some } from "lodash";

export class WeedDetector
  extends React.Component<WeedDetectorProps, Partial<WeedDetectorState>> {

  constructor(props: WeedDetectorProps) {
    super(props);
    this.state = { remoteFarmwareSettings: {} };
  }

  /** Delete all map points created by the Weed Detector. */
  clearWeeds = () => {
    const progress = (p: Readonly<Progress>) => {
      const percentage = `${Math.round((p.completed / p.total) * 100)} %`;
      this.setState({ deletionProgress: p.isDone ? "" : percentage });
    };
    this.props.dispatch(deletePoints(t("weeds"),
      { meta: { created_by: "plant-detection" } }, progress));
    this.setState({ deletionProgress: t("Deleting...") });
  };

  namespace = namespace<WEED_DETECTOR_KEY_PART>("WEED_DETECTOR_");

  change = (key: NumericKeyName, value: number) => {
    this.saveEnvVar(this.namespace(key), value);
  };

  saveEnvVar = (key: WDENVKey, value: number) =>
    this.props.dispatch(this.props.saveFarmwareEnv(
      key, JSON.stringify(formatEnvKey(key, value))));

  wdEnvGet = (key: WDENVKey) => envGet(key, this.props.wDEnv);

  getDefault = (key: WEED_DETECTOR_KEY_PART) =>
    WD_KEY_DEFAULTS[this.namespace(key)];

  getLabeledDefault = (key: WEED_DETECTOR_KEY_PART) =>
    SPECIAL_VALUE_DDI()[this.getDefault(key)].label;

  get commonProps() {
    return {
      wdEnvGet: this.wdEnvGet,
      onChange: this.saveEnvVar,
    };
  }

  get anyAdvancedModified() {
    return some(["save_detected_plants"].map((key: NumericKeyName) =>
      this.getDefault(key) != this.wdEnvGet(this.namespace(key))));
  }

  render() {
    const wDEnvGet = (key: WDENVKey) => envGet(key, this.props.wDEnv);
    const { syncStatus, botToMqttStatus } = this.props;
    const botOnline = isBotOnline(syncStatus, botToMqttStatus);
    const camDisabled = cameraBtnProps(this.props.env, botOnline);
    return <div className="weed-detector">
      <div className="row grid-exp-1">
        <div />
        <button
          title={t("clear weeds")}
          onClick={this.clearWeeds}
          className="fb-button red">
          {this.state.deletionProgress || t("CLEAR WEEDS")}
        </button>
        <MustBeOnline
          syncStatus={this.props.syncStatus}
          networkState={this.props.botToMqttStatus}
          hideBanner={true}>
          <button
            className={`fb-button green ${camDisabled.class}`}
            title={camDisabled.title}
            onClick={camDisabled.click ||
              detectPlants(wDEnvGet("CAMERA_CALIBRATION_coord_scale"))}>
            {t("detect weeds")}
          </button>
        </MustBeOnline>
      </div>
      <ImageWorkspace
        sectionKey={"detection"}
        dispatch={this.props.dispatch}
        advancedSectionOpen={this.props.photosPanelState.detectionPP}
        botOnline={
          isBotOnline(this.props.syncStatus, this.props.botToMqttStatus)}
        onProcessPhoto={scanImage(wDEnvGet("CAMERA_CALIBRATION_coord_scale"))}
        currentImage={this.props.currentImage}
        images={this.props.images}
        onChange={this.change}
        timeSettings={this.props.timeSettings}
        showAdvanced={this.props.showAdvanced}
        namespace={this.namespace}
        iteration={wDEnvGet(this.namespace("iteration"))}
        morph={wDEnvGet(this.namespace("morph"))}
        blur={wDEnvGet(this.namespace("blur"))}
        H_LO={wDEnvGet(this.namespace("H_LO"))}
        H_HI={wDEnvGet(this.namespace("H_HI"))}
        S_LO={wDEnvGet(this.namespace("S_LO"))}
        S_HI={wDEnvGet(this.namespace("S_HI"))}
        V_LO={wDEnvGet(this.namespace("V_LO"))}
        V_HI={wDEnvGet(this.namespace("V_HI"))} />
      <div className={"grid"}>
        <BoolConfig {...this.commonProps}
          settingName={DeviceSetting.saveDetectedPlants}
          advanced={true}
          showAdvanced={this.props.showAdvanced}
          modified={this.anyAdvancedModified}
          helpText={t(ToolTips.SAVE_DETECTED_PLANTS, {
            defaultSavePlants:
              this.getLabeledDefault("save_detected_plants")
          })}
          configKey={this.namespace("save_detected_plants")} />
        <BoolConfig {...this.commonProps}
          settingName={DeviceSetting.ignoreDetectionsOutOfBounds}
          helpText={t(ToolTips.USE_BOUNDS, {
            defaultUseBounds: this.getLabeledDefault("use_bounds")
          })}
          configKey={this.namespace("use_bounds")} />
        <NumberBoxConfig {...this.commonProps}
          settingName={DeviceSetting.minimumWeedSize}
          configKey={this.namespace("min_radius")}
          scale={2}
          helpText={t(ToolTips.MIN_RADIUS, {
            defaultMinDiameter: this.getDefault("min_radius") * 2
          })} />
        <NumberBoxConfig {...this.commonProps}
          settingName={DeviceSetting.maximumWeedSize}
          configKey={this.namespace("max_radius")}
          scale={2}
          helpText={t(ToolTips.MAX_RADIUS, {
            defaultMaxDiameter: this.getDefault("max_radius") * 2
          })} />
      </div>
    </div>;
  }
}