273 lines
10 KiB
TypeScript
273 lines
10 KiB
TypeScript
import * as React from "react";
|
|
import { connect } from "react-redux";
|
|
import {
|
|
Page, Row, LeftPanel, CenterPanel, RightPanel, DocSlug,
|
|
} from "../ui/index";
|
|
import { mapStateToProps, isPendingInstallation } from "./state_to_props";
|
|
import { Photos } from "./images/photos";
|
|
import { CameraCalibration } from "./camera_calibration/camera_calibration";
|
|
import { FarmwareProps } from "../devices/interfaces";
|
|
import { WeedDetector } from "./weed_detector/index";
|
|
import { envGet } from "./weed_detector/remote_env/selectors";
|
|
import { setActiveFarmwareByName } from "./set_active_farmware_by_name";
|
|
import { FarmwareList } from "./farmware_list";
|
|
import {
|
|
FarmwareForm, needsFarmwareForm, farmwareHelpText,
|
|
} from "./farmware_forms";
|
|
import { urlFriendly } from "../util";
|
|
import { ToolTips, Actions } from "../constants";
|
|
import { FarmwareInfo } from "./farmware_info";
|
|
import { Farmwares, FarmwareManifestInfo } from "./interfaces";
|
|
import { commandErr } from "../devices/actions";
|
|
import { getDevice } from "../device";
|
|
import { t } from "../i18next_wrapper";
|
|
import { isBotOnline } from "../devices/must_be_online";
|
|
import { BooleanSetting } from "../session_keys";
|
|
import { Dictionary } from "farmbot";
|
|
import { WDENVKey } from "./weed_detector/remote_env/interfaces";
|
|
|
|
/** Get the correct help text for the provided Farmware. */
|
|
const getToolTipByFarmware =
|
|
(farmwares: Farmwares, farmwareName: string | undefined) => {
|
|
if (farmwareName) {
|
|
switch (urlFriendly(farmwareName).replace("-", "_")) {
|
|
case "take_photo":
|
|
case "photos":
|
|
return ToolTips.PHOTOS;
|
|
case "camera_calibration":
|
|
return ToolTips.CAMERA_CALIBRATION;
|
|
case "plant_detection":
|
|
case "weed_detector":
|
|
return ToolTips.WEED_DETECTOR;
|
|
default:
|
|
return farmwareHelpText(getFarmwareByName(farmwares, farmwareName));
|
|
}
|
|
} else {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
/** Get a documentation link for the provided Farmware if one exists. */
|
|
const getDocLinkByFarmware =
|
|
(farmwareName: string | undefined): DocSlug | undefined => {
|
|
if (farmwareName) {
|
|
switch (urlFriendly(farmwareName).replace("-", "_")) {
|
|
case "camera_calibration":
|
|
return "camera-calibration";
|
|
case "plant_detection":
|
|
case "weed_detector":
|
|
return "weed-detection";
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Get Farmware details for the provided Farmware name. */
|
|
const getFarmwareByName =
|
|
(farmwares: Farmwares, farmwareName: string | undefined) => {
|
|
switch (farmwareName) {
|
|
case "Photos":
|
|
return farmwares["take-photo"];
|
|
case "Camera Calibration":
|
|
return farmwares["camera-calibration"];
|
|
case "Weed Detector":
|
|
return farmwares["plant-detection"];
|
|
default:
|
|
return farmwares[farmwareName || ""];
|
|
}
|
|
};
|
|
|
|
const FARMWARE_NAMES_1ST_PARTY: Dictionary<string> = {
|
|
"take-photo": "Photos",
|
|
"camera-calibration": "Camera Calibration",
|
|
"plant-detection": "Weed Detector",
|
|
};
|
|
|
|
export const getFormattedFarmwareName = (farmwareName: string) =>
|
|
FARMWARE_NAMES_1ST_PARTY[farmwareName] || farmwareName;
|
|
|
|
export const farmwareUrlFriendly = (farmwareName: string) =>
|
|
urlFriendly(farmwareName).replace(/-/g, "_");
|
|
|
|
/** Execute a Farmware. */
|
|
const run = (farmwareName: string) => () => {
|
|
getDevice().execScript(farmwareName)
|
|
.then(() => { }, commandErr("Farmware execution"));
|
|
};
|
|
|
|
interface BasicFarmwarePageProps {
|
|
farmwareName: string;
|
|
farmware: FarmwareManifestInfo | undefined;
|
|
botOnline: boolean;
|
|
}
|
|
|
|
export const BasicFarmwarePage = ({ farmwareName, farmware, botOnline }:
|
|
BasicFarmwarePageProps) =>
|
|
<div className={"basic-farmware-page"}>
|
|
<button
|
|
className="fb-button green farmware-button"
|
|
disabled={isPendingInstallation(farmware) || !botOnline}
|
|
title={t("Run Farmware")}
|
|
onClick={run(farmwareName)}>
|
|
{t("Run")}
|
|
</button>
|
|
<p>
|
|
{isPendingInstallation(farmware)
|
|
? t("Pending installation.")
|
|
: t("No inputs provided.")}
|
|
</p>
|
|
</div>;
|
|
|
|
export class RawFarmwarePage extends React.Component<FarmwareProps, {}> {
|
|
get current() { return this.props.currentFarmware; }
|
|
|
|
get botOnline() {
|
|
return isBotOnline(this.props.syncStatus, this.props.botToMqttStatus);
|
|
}
|
|
|
|
componentDidMount() {
|
|
if (window.innerWidth > 450) {
|
|
this.props.dispatch({
|
|
type: Actions.SELECT_FARMWARE,
|
|
payload: "Photos"
|
|
});
|
|
}
|
|
const farmwareNames = Object.values(this.props.farmwares).map(x => x.name)
|
|
.concat(Object.keys(FARMWARE_NAMES_1ST_PARTY));
|
|
setActiveFarmwareByName(farmwareNames);
|
|
}
|
|
|
|
/** Load Farmware input panel contents for 1st & 3rd party Farmware. */
|
|
getPanelByFarmware(farmwareName: string) {
|
|
const wDEnvGet = (key: WDENVKey) => envGet(key, this.props.wDEnv);
|
|
switch (farmwareUrlFriendly(farmwareName)) {
|
|
case "take_photo":
|
|
case "photos":
|
|
return <Photos
|
|
syncStatus={this.props.syncStatus}
|
|
botToMqttStatus={this.props.botToMqttStatus}
|
|
timeSettings={this.props.timeSettings}
|
|
dispatch={this.props.dispatch}
|
|
images={this.props.images}
|
|
env={this.props.env}
|
|
currentImage={this.props.currentImage}
|
|
imageJobs={this.props.imageJobs} />;
|
|
case "camera_calibration":
|
|
return <CameraCalibration
|
|
syncStatus={this.props.syncStatus}
|
|
dispatch={this.props.dispatch}
|
|
currentImage={this.props.currentImage}
|
|
images={this.props.images}
|
|
wDEnv={this.props.wDEnv}
|
|
env={this.props.env}
|
|
saveFarmwareEnv={this.props.saveFarmwareEnv}
|
|
iteration={wDEnvGet("CAMERA_CALIBRATION_iteration")}
|
|
morph={wDEnvGet("CAMERA_CALIBRATION_morph")}
|
|
blur={wDEnvGet("CAMERA_CALIBRATION_blur")}
|
|
H_LO={wDEnvGet("CAMERA_CALIBRATION_H_LO")}
|
|
S_LO={wDEnvGet("CAMERA_CALIBRATION_S_LO")}
|
|
V_LO={wDEnvGet("CAMERA_CALIBRATION_V_LO")}
|
|
H_HI={wDEnvGet("CAMERA_CALIBRATION_H_HI")}
|
|
S_HI={wDEnvGet("CAMERA_CALIBRATION_S_HI")}
|
|
V_HI={wDEnvGet("CAMERA_CALIBRATION_V_HI")}
|
|
timeSettings={this.props.timeSettings}
|
|
shouldDisplay={this.props.shouldDisplay}
|
|
botToMqttStatus={this.props.botToMqttStatus} />;
|
|
case "plant_detection":
|
|
case "weed_detector":
|
|
return <WeedDetector {...this.props} />;
|
|
default:
|
|
const farmware = getFarmwareByName(this.props.farmwares, farmwareName);
|
|
return farmware && needsFarmwareForm(farmware)
|
|
? <FarmwareForm farmware={farmware}
|
|
env={this.props.env}
|
|
shouldDisplay={this.props.shouldDisplay}
|
|
saveFarmwareEnv={this.props.saveFarmwareEnv}
|
|
botOnline={this.botOnline}
|
|
dispatch={this.props.dispatch} />
|
|
: <BasicFarmwarePage
|
|
farmwareName={farmwareName}
|
|
farmware={farmware}
|
|
botOnline={this.botOnline} />;
|
|
}
|
|
}
|
|
|
|
FarmwareBackButton = (props: { className: string }) => {
|
|
const infoOpen = props.className.includes("farmware-info-open");
|
|
return <i
|
|
className={`back-to-farmware fa fa-arrow-left ${props.className}`}
|
|
onClick={() => infoOpen
|
|
? this.props.dispatch({
|
|
type: Actions.SET_FARMWARE_INFO_STATE, payload: false
|
|
})
|
|
: this.props.dispatch({
|
|
type: Actions.SELECT_FARMWARE, payload: undefined
|
|
})}
|
|
title={infoOpen ? t("back to farmware") : t("back to farmware list")} />;
|
|
};
|
|
|
|
FarmwareInfoButton = (props: { className: string, online: boolean }) =>
|
|
<button
|
|
className={`farmware-info-button fb-button gray ${props.className}`}
|
|
disabled={!props.online}
|
|
title={t("open Farmware info")}
|
|
onClick={() => this.props.dispatch({
|
|
type: Actions.SET_FARMWARE_INFO_STATE, payload: true
|
|
})}>
|
|
{t("farmware info")}
|
|
</button>
|
|
|
|
render() {
|
|
const farmware = getFarmwareByName(
|
|
this.props.farmwares, this.current || "take-photo");
|
|
const farmwareOpen = this.current ? "open" : "";
|
|
const online = this.props.botToMqttStatus === "up";
|
|
const infoOpen = (this.props.infoOpen && online) ? "farmware-info-open" : "";
|
|
const activeClasses = [farmwareOpen, infoOpen].join(" ");
|
|
const showFirstParty =
|
|
!!this.props.getConfigValue(BooleanSetting.show_first_party_farmware);
|
|
return <Page className="farmware-page">
|
|
<Row>
|
|
<LeftPanel
|
|
className={`farmware-list-panel ${activeClasses}`}
|
|
title={t("Farmware")}>
|
|
<FarmwareList
|
|
current={this.current}
|
|
dispatch={this.props.dispatch}
|
|
shouldDisplay={this.props.shouldDisplay}
|
|
farmwares={this.props.farmwares}
|
|
installations={this.props.taggedFarmwareInstallations}
|
|
firstPartyFarmwareNames={this.props.firstPartyFarmwareNames}
|
|
showFirstParty={showFirstParty} />
|
|
</LeftPanel>
|
|
<CenterPanel
|
|
className={`farmware-input-panel ${activeClasses}`}
|
|
backButton={<this.FarmwareBackButton className={activeClasses} />}
|
|
title={getFormattedFarmwareName(this.current || "Photos")}>
|
|
{<div className="farmware-input-panel-contents">
|
|
<this.FarmwareInfoButton className={activeClasses} online={online} />
|
|
{this.getPanelByFarmware(this.current ? this.current : "photos")}
|
|
</div>}
|
|
</CenterPanel>
|
|
<RightPanel
|
|
className={`farmware-info-panel ${activeClasses}`}
|
|
backButton={<this.FarmwareBackButton className={activeClasses} />}
|
|
title={t("Information")}
|
|
helpText={getToolTipByFarmware(this.props.farmwares, this.current)
|
|
|| ToolTips.PHOTOS}
|
|
docPage={getDocLinkByFarmware(this.current)}
|
|
show={!!farmware}>
|
|
<FarmwareInfo
|
|
dispatch={this.props.dispatch}
|
|
farmware={farmware}
|
|
installations={this.props.taggedFarmwareInstallations}
|
|
shouldDisplay={this.props.shouldDisplay}
|
|
firstPartyFarmwareNames={this.props.firstPartyFarmwareNames}
|
|
showFirstParty={showFirstParty} />
|
|
</RightPanel>
|
|
</Row>
|
|
</Page>;
|
|
}
|
|
}
|
|
|
|
export const FarmwarePage = connect(mapStateToProps)(RawFarmwarePage);
|