update pi GPIO pin restrictions

pull/1440/head
gabrielburnworth 2019-09-16 14:21:55 -07:00
parent 12eebc9f44
commit d2ae4ff300
9 changed files with 63 additions and 55 deletions

View File

@ -1,5 +1,7 @@
class PinBinding < ApplicationRecord
OFF_LIMITS = [ 6, 12, 13, 17, 21, 23, 24, 25, 27 ]
OFF_LIMITS = [
2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 24, 25, 27
]
BAD_PIN_NUM = \
"The following pin numbers cannot be used: %s" % OFF_LIMITS.join(", ")
@ -20,7 +22,7 @@ class PinBinding < ApplicationRecord
end
def random_pin_num
[*(0..69)]
[*(0..27)]
.without(*OFF_LIMITS)
.without(*device.pin_bindings.pluck(:pin_num))
.sample

View File

@ -11,12 +11,16 @@ describe("sortByNameAndPin()", () => {
const btn1Pin = ButtonPin.estop;
const btn2Pin = ButtonPin.unlock;
const GPIO_SM = 4;
const GPIO_LG = 18;
const sortTest = (first: number, second: number, order: Order) =>
expect(sortByNameAndPin(first, second)).toEqual(order);
it("sorts", () => {
sortTest(btn1Pin, 10, Order.firstSmaller); // Button 1 < GPIO 10
sortTest(2, 10, Order.firstSmaller); // GPIO 2 < GPIO 10
sortTest(btn1Pin, 1, Order.firstSmaller); // Button 1 < GPIO 1
sortTest(GPIO_SM, GPIO_LG, Order.firstSmaller); // GPIO SM < GPIO LG
sortTest(GPIO_LG, GPIO_SM, Order.secondSmaller); // GPIO LG > GPIO SM
sortTest(btn1Pin, btn2Pin, Order.firstSmaller); // Button 1 < Button 2
sortTest(btn2Pin, btn1Pin, Order.secondSmaller); // Button 2 > Button 1
sortTest(1, 1, Order.equal); // GPIO 1 == GPIO 1

View File

@ -1,14 +1,10 @@
const mockDevice = {
registerGpio: jest.fn(() => { return Promise.resolve(); }),
unregisterGpio: jest.fn(() => { return Promise.resolve(); }),
registerGpio: jest.fn(() => Promise.resolve()),
unregisterGpio: jest.fn(() => Promise.resolve()),
};
jest.mock("../../../device", () => ({
getDevice: () => (mockDevice)
}));
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
jest.mock("../../../api/crud", () => ({
initSave: jest.fn()
}));
jest.mock("../../../api/crud", () => ({ initSave: jest.fn() }));
import * as React from "react";
import { mount, shallow } from "enzyme";
@ -33,6 +29,8 @@ import {
} from "farmbot/dist/resources/api_resources";
import { error, warning } from "../../../toast/toast";
const AVAILABLE_PIN = 18;
describe("<PinBindingInputGroup/>", () => {
function fakeProps(): PinBindingInputGroupProps {
const fakeResources: TaggedSequence[] = [fakeSequence(), fakeSequence()];
@ -43,8 +41,8 @@ describe("<PinBindingInputGroup/>", () => {
const resources = buildResourceIndex(fakeResources).index;
return {
pinBindings: [
{ pin_number: 10, sequence_id: 1 },
{ pin_number: 11, sequence_id: 2 },
{ pin_number: 4, sequence_id: 1 },
{ pin_number: 5, sequence_id: 2 },
],
dispatch: jest.fn(),
resources: resources,
@ -69,7 +67,7 @@ describe("<PinBindingInputGroup/>", () => {
const wrapper = mount(<PinBindingInputGroup {...fakeProps()} />);
const buttons = wrapper.find("button");
expect(buttons.last().text()).toEqual("BIND");
wrapper.setState({ pinNumberInput: 7 });
wrapper.setState({ pinNumberInput: AVAILABLE_PIN });
buttons.last().simulate("click");
expect(error).toHaveBeenCalledWith("Please select a sequence or action.");
});
@ -98,7 +96,7 @@ describe("<PinBindingInputGroup/>", () => {
const buttons = wrapper.find("button");
expect(buttons.last().text()).toEqual("BIND");
wrapper.setState({
pinNumberInput: 2,
pinNumberInput: 0,
bindingType: PinBindingType.special,
sequenceIdInput: undefined,
specialActionInput: PinBindingSpecialAction.emergency_lock
@ -107,7 +105,7 @@ describe("<PinBindingInputGroup/>", () => {
expect(mockDevice.registerGpio).not.toHaveBeenCalled();
expect(initSave).toHaveBeenCalledWith("PinBinding",
{
pin_num: 2,
pin_num: 0,
binding_type: PinBindingType.special,
special_action: PinBindingSpecialAction.emergency_lock
});
@ -125,15 +123,16 @@ describe("<PinBindingInputGroup/>", () => {
});
it("sets pin", () => {
const wrapper = mount<PinBindingInputGroup>(<PinBindingInputGroup
{...fakeProps()} />);
const p = fakeProps();
const wrapper = mount<PinBindingInputGroup>(<PinBindingInputGroup {...p} />);
expect(wrapper.instance().state.pinNumberInput).toEqual(undefined);
wrapper.instance().setSelectedPin(10); // pin already bound
const { pin_number } = p.pinBindings[0];
wrapper.instance().setSelectedPin(pin_number); // pin already bound
expect(wrapper.instance().state.pinNumberInput).toEqual(undefined);
wrapper.instance().setSelectedPin(99); // invalid pin
expect(wrapper.instance().state.pinNumberInput).toEqual(undefined);
wrapper.instance().setSelectedPin(5); // available pin
expect(wrapper.instance().state.pinNumberInput).toEqual(5);
wrapper.instance().setSelectedPin(AVAILABLE_PIN); // available pin
expect(wrapper.instance().state.pinNumberInput).toEqual(AVAILABLE_PIN);
wrapper.instance().setSelectedPin(1); // reserved pin
expect(wrapper.instance().state.pinNumberInput).toEqual(1);
expect(warning).toHaveBeenCalledWith(
@ -144,8 +143,8 @@ describe("<PinBindingInputGroup/>", () => {
const wrapper = shallow<PinBindingInputGroup>(<PinBindingInputGroup
{...fakeProps()} />);
expect(wrapper.instance().state.pinNumberInput).toEqual(undefined);
wrapper.instance().setSelectedPin(7);
expect(wrapper.instance().state.pinNumberInput).toEqual(7);
wrapper.instance().setSelectedPin(AVAILABLE_PIN);
expect(wrapper.instance().state.pinNumberInput).toEqual(AVAILABLE_PIN);
});
it("changes binding type", () => {
@ -177,8 +176,10 @@ describe("<PinNumberInputGroup />", () => {
pinNumberInput={undefined}
boundPins={[]}
setSelectedPin={setSelectedPin} />);
wrapper.find("FBSelect").simulate("change", { label: "", value: 7 });
expect(setSelectedPin).toHaveBeenCalledWith(7);
wrapper.find("FBSelect").simulate("change", {
label: "", value: AVAILABLE_PIN
});
expect(setSelectedPin).toHaveBeenCalledWith(AVAILABLE_PIN);
});
});
@ -207,7 +208,7 @@ describe("<ActionTargetDropDown />", () => {
});
describe("<SequenceTargetDropDown />", () => {
it("sets action", () => {
it("sets sequence ID", () => {
const setSequenceIdInput = jest.fn();
const wrapper = shallow(<SequenceTargetDropDown
sequenceIdInput={undefined}

View File

@ -1,18 +1,14 @@
const mockDevice = {
registerGpio: jest.fn(() => Promise.resolve()),
unregisterGpio: jest.fn(() => Promise.resolve()),
};
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
jest.mock("../../../api/crud", () => ({ destroy: jest.fn() }));
import {
PinBindingType, PinBindingSpecialAction
} from "farmbot/dist/resources/api_resources";
const mockDevice = {
registerGpio: jest.fn(() => { return Promise.resolve(); }),
unregisterGpio: jest.fn(() => { return Promise.resolve(); }),
};
jest.mock("../../../device", () => ({
getDevice: () => (mockDevice)
}));
jest.mock("../../../api/crud", () => ({
destroy: jest.fn()
}));
const mockData = [{
pin_number: 1, sequence_id: undefined,
special_action: PinBindingSpecialAction.sync,

View File

@ -4,13 +4,11 @@ import { RpiGpioDiagram, RpiGpioDiagramProps } from "../rpi_gpio_diagram";
import { Color } from "../../../ui/index";
describe("<RpiGpioDiagram />", () => {
function fakeProps(): RpiGpioDiagramProps {
return {
boundPins: [27],
setSelectedPin: jest.fn(),
selectedPin: undefined
};
}
const fakeProps = (): RpiGpioDiagramProps => ({
boundPins: [27],
setSelectedPin: jest.fn(),
selectedPin: undefined
});
it("renders", () => {
const wrapper = mount(<RpiGpioDiagram {...fakeProps()} />);

View File

@ -1,4 +1,3 @@
import {
PinBindingType,
PinBindingSpecialAction
@ -61,9 +60,16 @@ enum LEDPin {
/** Other pins used by FarmBot OS that cannot be used in pin bindings. */
enum SystemPins {
sda = 2,
scl = 3,
reset = 19,
i2c1_sda = 2,
i2c1_scl = 3,
spi0_ce1 = 7,
spi0_ce0 = 8,
spi0_miso = 9,
spi0_mosi = 10,
spi0_sclk = 11,
uart_tx = 14,
uart_rx = 15,
reset_2560 = 19,
}
const toPinNum = (n: string) => parseInt(n);
@ -73,9 +79,11 @@ const otherSysBindings: number[] = Object.values(SystemPins).map(toPinNum);
/** All pin numbers used by FarmBot OS that cannot be used in pin bindings. */
export const sysBindings = sysLedBindings.concat(sysBtnBindings, otherSysBindings);
const piI2cPins = [0, 1];
const piI2c0Pins = [0, 1];
export const piSpi0Pins = [7, 8, 9, 10, 11];
export const piSpi1Pins = [16, 17, 18, 19, 20, 21];
/** Pin numbers used for special purposes by the RPi. (internal pullup, etc.) */
export const reservedPiGPIO = piI2cPins;
export const reservedPiGPIO = piI2c0Pins;
const LabeledGpioPins: { [x: number]: string } = {
[ButtonPin.estop]: "Button 1: E-STOP",

View File

@ -1,5 +1,4 @@
import * as React from "react";
import { Row, Col, FBSelect, NULL_CHOICE, DropDownItem } from "../../ui";
import { PinBindingColWidth } from "./pin_bindings";
import { Popover, Position } from "@blueprintjs/core";

View File

@ -36,7 +36,7 @@ describe Api::PinBindingsController do
it 'creates a pin binding' do
sign_in user
s = FakeSequence.create( device: device)
input = { pin_num: 10, sequence_id: s.id}
input = { pin_num: 4, sequence_id: s.id}
b4 = PinBinding.count
post :create, body: input.to_json, params: { format: :json}
expect(response.status).to eq(200)

View File

@ -64,7 +64,7 @@ describe Api::SequencesController do
sign_in user
pb = PinBindings::Create.run!(device: user.device,
sequence_id: sequence.id,
pin_num: 10)
pin_num: 4)
delete :destroy, params: { id: sequence.id }
expect(response.status).to eq(422)
expect(json[:sequence]).to include("in use")