pin binding cleanup

pull/917/head
gabrielburnworth 2018-07-18 18:35:01 -07:00
parent 4372a2f482
commit afe40e5058
4 changed files with 117 additions and 71 deletions

View File

@ -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,

View File

@ -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>

View File

@ -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)

View File

@ -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,