pin binding cleanup
parent
4372a2f482
commit
afe40e5058
|
@ -3,6 +3,7 @@ import { PinBindingType, PinBindingSpecialAction } from "./interfaces";
|
|||
import { DropDownItem } from "../../ui";
|
||||
import { gpio } from "./rpi_gpio_diagram";
|
||||
import { flattenDeep, isNumber } from "lodash";
|
||||
import { ShouldDisplay, Feature } from "../interfaces";
|
||||
|
||||
export const bindingTypeLabelLookup: { [x: string]: string } = {
|
||||
[PinBindingType.standard]: t("Sequence"),
|
||||
|
@ -10,6 +11,14 @@ export const bindingTypeLabelLookup: { [x: string]: string } = {
|
|||
"": t("Sequence"),
|
||||
};
|
||||
|
||||
export const bindingTypeList = (shouldDisplay: ShouldDisplay): DropDownItem[] =>
|
||||
Object.entries(bindingTypeLabelLookup)
|
||||
.filter(([value, _]) => !(value == ""))
|
||||
.filter(([value, _]) =>
|
||||
shouldDisplay(Feature.api_pin_bindings)
|
||||
|| !(value == PinBindingType.special))
|
||||
.map(([value, label]) => ({ label, value }));
|
||||
|
||||
export const specialActionLabelLookup: { [x: string]: string } = {
|
||||
[PinBindingSpecialAction.emergency_lock]: t("E-STOP"),
|
||||
[PinBindingSpecialAction.emergency_unlock]: t("UNLOCK"),
|
||||
|
@ -27,6 +36,7 @@ export const specialActionList: DropDownItem[] =
|
|||
.map((action: PinBindingSpecialAction) =>
|
||||
({ label: specialActionLabelLookup[action], value: action }));
|
||||
|
||||
/** Pin numbers for standard buttons. */
|
||||
enum ButtonPin {
|
||||
estop = 16,
|
||||
unlock = 22,
|
||||
|
@ -35,6 +45,7 @@ enum ButtonPin {
|
|||
btn5 = 20,
|
||||
}
|
||||
|
||||
/** Pin numbers used for LED control; cannot be used in a pin binding. */
|
||||
enum LEDPin {
|
||||
sync = 24,
|
||||
connection = 25,
|
||||
|
@ -48,10 +59,13 @@ enum LEDPin {
|
|||
}
|
||||
|
||||
const sysLedBindings = Object.values(LEDPin);
|
||||
/** Pin numbers reserved for built-in pin bindings. */
|
||||
export const sysBtnBindings = [ButtonPin.estop, ButtonPin.unlock];
|
||||
/** All pin numbers used by FarmBot OS that cannot be used in pin bindings. */
|
||||
export const sysBindings = sysLedBindings.concat(sysBtnBindings);
|
||||
|
||||
const piI2cPins = [0, 1, 2, 3];
|
||||
/** Pin numbers used for special purposes by the RPi. (internal pullup, etc.) */
|
||||
export const reservedPiGPIO = piI2cPins;
|
||||
|
||||
const LabeledGpioPins: { [x: number]: string } = {
|
||||
|
@ -67,12 +81,14 @@ export const generatePinLabel = (pin: number) =>
|
|||
? `${LabeledGpioPins[pin]} (Pi ${pin})`
|
||||
: `Pi GPIO ${pin}`;
|
||||
|
||||
/** Raspberry Pi GPIO pin numbers. */
|
||||
export const validGpioPins: number[] =
|
||||
flattenDeep(gpio)
|
||||
.filter(x => isNumber(x))
|
||||
.map((x: number) => x);
|
||||
// .filter(n => !reservedPiGPIO.includes(n));
|
||||
|
||||
/** Sort fn for pin numbers using their labels. */
|
||||
export const sortByNameAndPin = (a: number, b: number) => {
|
||||
const aLabel = generatePinLabel(a).slice(0, 8);
|
||||
const bLabel = generatePinLabel(b).slice(0, 8);
|
||||
|
@ -85,6 +101,7 @@ export const sortByNameAndPin = (a: number, b: number) => {
|
|||
return 0;
|
||||
};
|
||||
|
||||
/** Given a list of bound pins, return a list of available pins (DDIs). */
|
||||
export const RpiPinList = (taken: number[]): DropDownItem[] =>
|
||||
validGpioPins
|
||||
.filter(n => !sysBindings.includes(n))
|
||||
|
@ -93,6 +110,7 @@ export const RpiPinList = (taken: number[]): DropDownItem[] =>
|
|||
.sort(sortByNameAndPin)
|
||||
.map(n => ({ label: generatePinLabel(n), value: n }));
|
||||
|
||||
/** FarmBot OS built-in pin binding data used by Pin Bindings widget. */
|
||||
export const sysBtnBindingData = [
|
||||
{
|
||||
pin_number: ButtonPin.estop,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { t } from "i18next";
|
||||
import { Row, Col, DropDownItem, FBSelect, NULL_CHOICE } from "../../ui";
|
||||
import { Row, Col, FBSelect, NULL_CHOICE } from "../../ui";
|
||||
import { PinBindingColWidth } from "./pin_bindings";
|
||||
import { Popover, Position } from "@blueprintjs/core";
|
||||
import { RpiGpioDiagram } from "./rpi_gpio_diagram";
|
||||
|
@ -16,7 +16,9 @@ import { registerGpioPin } from "../actions";
|
|||
import { error, warning } from "farmbot-toastr";
|
||||
import {
|
||||
validGpioPins, sysBindings, generatePinLabel, RpiPinList,
|
||||
bindingTypeLabelLookup, specialActionLabelLookup, specialActionList, reservedPiGPIO
|
||||
bindingTypeLabelLookup, specialActionLabelLookup, specialActionList,
|
||||
reservedPiGPIO,
|
||||
bindingTypeList
|
||||
} from "./list_and_label_support";
|
||||
import { SequenceSelectBox } from "../../sequences/sequence_select_box";
|
||||
|
||||
|
@ -30,10 +32,7 @@ export class PinBindingInputGroup
|
|||
bindingType: PinBindingType.standard,
|
||||
};
|
||||
|
||||
changeSelection = (input: DropDownItem) => {
|
||||
this.setState({ sequenceIdInput: parseInt("" + input.value) });
|
||||
}
|
||||
|
||||
/** Validate and provide warnings about a selected pin number. */
|
||||
setSelectedPin = (pin: number | undefined) => {
|
||||
if (!includes(this.boundPins, pin)) {
|
||||
if (includes(validGpioPins, pin)) {
|
||||
|
@ -49,11 +48,13 @@ export class PinBindingInputGroup
|
|||
}
|
||||
}
|
||||
|
||||
get boundPins(): number[] | undefined {
|
||||
/** Generate a list of unavailable pin numbers. */
|
||||
get boundPins(): number[] {
|
||||
const userBindings = this.props.pinBindings.map(x => x.pin_number);
|
||||
return userBindings.concat(sysBindings);
|
||||
}
|
||||
|
||||
/** Validate and save a pin binding. */
|
||||
bindPin = () => {
|
||||
const { shouldDisplay, dispatch } = this.props;
|
||||
const {
|
||||
|
@ -94,80 +95,106 @@ export class PinBindingInputGroup
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
pinNumberInput, sequenceIdInput, bindingType, specialActionInput
|
||||
} = this.state;
|
||||
/** pin number selection */
|
||||
pinNumberInputGroup = () => {
|
||||
const { pinNumberInput } = this.state;
|
||||
|
||||
const selectedPinNumber = isNumber(pinNumberInput) ? {
|
||||
label: generatePinLabel(pinNumberInput),
|
||||
value: "" + pinNumberInput
|
||||
} : NULL_CHOICE;
|
||||
|
||||
return <Row>
|
||||
<Col xs={1}>
|
||||
<Popover position={Position.TOP}>
|
||||
<i className="fa fa-th-large" />
|
||||
<RpiGpioDiagram
|
||||
boundPins={this.boundPins}
|
||||
setSelectedPin={this.setSelectedPin}
|
||||
selectedPin={this.state.pinNumberInput} />
|
||||
</Popover>
|
||||
</Col>
|
||||
<Col xs={9}>
|
||||
<FBSelect
|
||||
key={"pin_number_input_" + pinNumberInput}
|
||||
onChange={ddi =>
|
||||
this.setSelectedPin(parseInt("" + ddi.value))}
|
||||
selectedItem={selectedPinNumber}
|
||||
list={RpiPinList(this.boundPins)} />
|
||||
</Col>
|
||||
</Row>;
|
||||
}
|
||||
|
||||
/** binding type selection: sequence or action */
|
||||
bindingTypeDropDown = () => {
|
||||
const { bindingType } = this.state;
|
||||
const { shouldDisplay } = this.props;
|
||||
return <FBSelect
|
||||
key={"binding_type_input_" + bindingType}
|
||||
onChange={(ddi: { label: string, value: PinBindingType }) =>
|
||||
this.setState({
|
||||
bindingType: ddi.value,
|
||||
sequenceIdInput: undefined,
|
||||
specialActionInput: undefined
|
||||
})}
|
||||
selectedItem={{
|
||||
label: bindingTypeLabelLookup[bindingType],
|
||||
value: bindingType
|
||||
}}
|
||||
list={bindingTypeList(shouldDisplay)} />;
|
||||
}
|
||||
|
||||
/** sequence selection */
|
||||
sequenceTargetDropDown = () => {
|
||||
const { sequenceIdInput } = this.state;
|
||||
return <SequenceSelectBox
|
||||
key={sequenceIdInput}
|
||||
onChange={ddi =>
|
||||
this.setState({ sequenceIdInput: parseInt("" + ddi.value) })}
|
||||
resources={this.props.resources}
|
||||
sequenceId={sequenceIdInput} />;
|
||||
}
|
||||
|
||||
/** special action selection */
|
||||
actionTargetDropDown = () => {
|
||||
const { specialActionInput } = this.state;
|
||||
|
||||
const setSpecialAction =
|
||||
(ddi: { label: string, value: PinBindingSpecialAction }) =>
|
||||
this.setState({ specialActionInput: ddi.value });
|
||||
|
||||
const selectedSpecialAction = specialActionInput ? {
|
||||
label: specialActionLabelLookup[specialActionInput || ""],
|
||||
value: "" + specialActionInput
|
||||
} : NULL_CHOICE;
|
||||
|
||||
return <FBSelect
|
||||
key={"special_action_input_" + specialActionInput}
|
||||
onChange={setSpecialAction}
|
||||
selectedItem={selectedSpecialAction}
|
||||
list={specialActionList} />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { bindingType } = this.state;
|
||||
|
||||
return <Row>
|
||||
<Col xs={PinBindingColWidth.pin}>
|
||||
<Row>
|
||||
<Col xs={1}>
|
||||
<Popover position={Position.TOP}>
|
||||
<i className="fa fa-th-large" />
|
||||
<RpiGpioDiagram
|
||||
boundPins={this.boundPins}
|
||||
setSelectedPin={this.setSelectedPin}
|
||||
selectedPin={this.state.pinNumberInput} />
|
||||
</Popover>
|
||||
</Col>
|
||||
<Col xs={9}>
|
||||
<FBSelect
|
||||
key={"pin_number_input_" + pinNumberInput}
|
||||
onChange={ddi =>
|
||||
this.setSelectedPin(parseInt("" + ddi.value))}
|
||||
selectedItem={isNumber(pinNumberInput)
|
||||
? {
|
||||
label: generatePinLabel(pinNumberInput),
|
||||
value: "" + pinNumberInput
|
||||
}
|
||||
: NULL_CHOICE}
|
||||
list={RpiPinList(this.boundPins || [])} />
|
||||
</Col>
|
||||
</Row>
|
||||
<this.pinNumberInputGroup />
|
||||
</Col>
|
||||
<Col xs={PinBindingColWidth.type}>
|
||||
<FBSelect
|
||||
key={"binding_type_input_" + pinNumberInput}
|
||||
onChange={(ddi: { label: string, value: PinBindingType }) =>
|
||||
this.setState({ bindingType: ddi.value })}
|
||||
selectedItem={{
|
||||
label: bindingTypeLabelLookup[bindingType],
|
||||
value: bindingType
|
||||
}}
|
||||
list={Object.entries(bindingTypeLabelLookup)
|
||||
.filter(([value, _]) => !(value == ""))
|
||||
.filter(([value, _]) =>
|
||||
shouldDisplay(Feature.api_pin_bindings)
|
||||
|| !(value == PinBindingType.special))
|
||||
.map(([value, label]) => ({ label, value }))} />
|
||||
<this.bindingTypeDropDown />
|
||||
</Col>
|
||||
<Col xs={PinBindingColWidth.target}>
|
||||
{bindingType == PinBindingType.special
|
||||
? <FBSelect
|
||||
key={"special_action_input_" + pinNumberInput}
|
||||
onChange={
|
||||
(ddi: { label: string, value: PinBindingSpecialAction }) =>
|
||||
this.setState({ specialActionInput: ddi.value })}
|
||||
selectedItem={specialActionInput
|
||||
? {
|
||||
label: specialActionLabelLookup[specialActionInput || ""],
|
||||
value: "" + specialActionInput
|
||||
}
|
||||
: NULL_CHOICE}
|
||||
list={specialActionList} />
|
||||
: <SequenceSelectBox
|
||||
key={sequenceIdInput}
|
||||
onChange={this.changeSelection}
|
||||
resources={this.props.resources}
|
||||
sequenceId={sequenceIdInput} />}
|
||||
? <this.actionTargetDropDown />
|
||||
: <this.sequenceTargetDropDown />}
|
||||
</Col>
|
||||
<Col xs={PinBindingColWidth.button}>
|
||||
<button
|
||||
className="fb-button green"
|
||||
type="button"
|
||||
onClick={() => { this.bindPin(); }} >
|
||||
onClick={this.bindPin} >
|
||||
{t("BIND")}
|
||||
</button>
|
||||
</Col>
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { t } from "i18next";
|
||||
import {
|
||||
Widget, WidgetBody, WidgetHeader,
|
||||
Row, Col,
|
||||
} from "../../ui/index";
|
||||
import { Widget, WidgetBody, WidgetHeader, Row, Col } from "../../ui/index";
|
||||
import { ToolTips } from "../../constants";
|
||||
import { Feature } from "../interfaces";
|
||||
import { selectAllPinBindings } from "../../resources/selectors";
|
||||
|
@ -16,6 +13,7 @@ import { sysBtnBindingData } from "./list_and_label_support";
|
|||
import { PinBindingsList } from "./pin_bindings_list";
|
||||
import { PinBindingInputGroup } from "./pin_binding_input_group";
|
||||
|
||||
/** Width of UI columns in Pin Bindings widget. */
|
||||
export enum PinBindingColWidth {
|
||||
pin = 4,
|
||||
type = 3,
|
||||
|
@ -23,6 +21,7 @@ export enum PinBindingColWidth {
|
|||
button = 1
|
||||
}
|
||||
|
||||
/** Use binding type to return a sequence ID or a special action. */
|
||||
const getBindingTarget = (bindingBody: PinBinding): {
|
||||
sequence_id: number | undefined,
|
||||
special_action: PinBindingSpecialAction | undefined
|
||||
|
@ -35,6 +34,7 @@ const getBindingTarget = (bindingBody: PinBinding): {
|
|||
export const PinBindings = (props: PinBindingsProps) => {
|
||||
const { dispatch, resources, shouldDisplay, botToMqttStatus, bot } = props;
|
||||
|
||||
/** Return pin binding data according to FBOS version. */
|
||||
const getPinBindings = (): PinBindingListItems[] => {
|
||||
if (shouldDisplay(Feature.api_pin_bindings)) {
|
||||
const userBindings = selectAllPinBindings(resources)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PinBindingType, PinBindingSpecialAction, PinBinding } from "./interfaces";
|
||||
import { TaggedPinBinding, SpecialStatus } from "../../resources/tagged_resources";
|
||||
|
||||
/** Return the correct Pin Binding resource according to binding type. */
|
||||
export const taggedPinBinding =
|
||||
(bodyInputs: {
|
||||
pin_num: number,
|
||||
|
|
Loading…
Reference in New Issue