[New] Wrote JavaDoc for all classes

This commit is contained in:
Robert von Burg 2016-02-12 12:45:44 +01:00
parent 77c3dd1854
commit ede059c07a
8 changed files with 362 additions and 34 deletions

View File

@ -1,5 +1,16 @@
package ch.eitchnet.beaglebone;
/**
* <p>
* Defines the direction of a {@link Gpio}
* </p>
*
* <p>
* A {@link Gpio} can be either an input pin, or an output pin. This is defined by this enum
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public enum Direction {
IN("in"), OUT("out");

View File

@ -1,14 +1,38 @@
package ch.eitchnet.beaglebone;
/**
* <p>
* Defines a {@link Gpio} using the combination of {@link Pin}, {@link Direction} and {@link Signal}
* </p>
*
* <p>
* {@link Gpio} are instantiated by the {@link GpioBridge} by calling {@link GpioBridge#getGpio(Pin, Direction)}
* </p>
*
* <p>
* The {@link Gpio}'s {@link Signal} is always updated by the {@link GpioBridge} when a new {@link Signal} is read or
* written.
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class Gpio {
private final Pin pin;
private final String kernelName;
private final Direction direction;
private String label;
private Signal signal;
private String label;
public Gpio(Pin pin, Direction direction) {
/**
* Constructs a new {@link Gpio}
*
* @param pin
* the {@link Pin}
* @param direction
* the {@link Direction}
*/
Gpio(Pin pin, Direction direction) {
this.pin = pin;
this.direction = direction;
this.kernelName = "gpio" + pin.getGpioNr();
@ -16,36 +40,76 @@ public class Gpio {
this.signal = Signal.LOW;
}
/**
* @return the reference to this {@link Gpio}'s {@link Pin}
*/
public Pin getPin() {
return this.pin;
}
/**
* @return the kernel name e.g. "gpio60"
*/
public String getKernelName() {
return this.kernelName;
}
/**
* Allows the user to set a label for this pin, e.g. "Green Button", "Red Led"
*
* @param label
* the label to set, e.g. "Green Button", "Red Led"
*
* @return this {@link Gpio} for call chaining
*/
public Gpio setLabel(String label) {
this.label = label;
return this;
}
/**
* @return the user configurable label of this pin, e.g. "Green Button", "Red Led"
*/
public String getLabel() {
return this.label;
}
/**
* @return the {@link Gpio}'s {@link Direction}
*/
public Direction getDirection() {
return this.direction;
}
/**
* @return the {@link Gpio}'s current {@link Signal}
*/
public Signal getSignal() {
return this.signal;
}
public Gpio setSignal(Signal signal) {
/**
* <p>
* THIS METHOD IS ONLY CALLED BY THE {@link GpioBridge}
* </p>
*
* <p>
* Set the {@link Gpio}'s current {@link Signal}
* </p>
*
* @param signal
* the new {@link Signal} to set
*
* @return this {@link Gpio} instance for call chaining
*/
Gpio setSignal(Signal signal) {
this.signal = signal;
return this;
}
/**
* @see Pin#toString()
*/
@Override
public String toString() {
return this.pin.toString();

View File

@ -12,8 +12,26 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* Main object to give access to GPIO ports on a Linux kernel
* </p>
*
* <p>
* The {@link GpioBridge} is a singleton. Features include retrieving Pins, writing and reading values, as well as
* registering observers for changes to input pins
* </p>
*
* <p>
* {@link Gpio} objects are cached and their {@link Signal} is set by the {@link GpioBridge} accordingly
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class GpioBridge {
private static final String GPIO_PATH = "/sys/class/gpio/";
private Map<Pin, Gpio> cache;
private Map<Gpio, List<GpioSignalListener>> listeners;
private Thread thread;
@ -25,49 +43,100 @@ public class GpioBridge {
instance = new GpioBridge();
}
/**
* @return the instance of the {@link GpioBridge}
*/
public static GpioBridge getInstance() {
return instance;
}
/**
* Private constructor for singleton construction
*/
private GpioBridge() {
this.cache = new HashMap<>();
this.listeners = Collections.synchronizedMap(new HashMap<>());
}
/**
* Returns the kernel file path to the value of the {@link Gpio}
*
* @param gpio
* the {@link Gpio} for which the path is to be returned
* @return the Path to the {@link Gpio}'s value
*/
private File getGpioValuePath(Gpio gpio) {
return new File(GpioBridgeTest.GPIO_PATH, gpio.getKernelName() + "/value");
return new File(GPIO_PATH, gpio.getKernelName() + "/value");
}
/**
* Returns the kernel file path to the direction of the {@link Gpio}
*
* @param gpio
* the {@link Gpio} for which the path is to be returned
* @return the Path to the {@link Gpio}'s direction
*/
private File getGpioDirectionPath(Gpio gpio) {
return new File(GpioBridgeTest.GPIO_PATH, gpio.getKernelName() + "/direction");
return new File(GPIO_PATH, gpio.getKernelName() + "/direction");
}
/**
* <p>
* Public API method to write the given {@link Signal} on the given {@link Gpio}'s pin.
* </p>
*
* @param gpio
* the {@link Gpio} to which the {@link Signal} should be written
* @param signal
* the {@link Signal} to write to the given {@link Gpio}
*
* @throws GpioException
* if the direction of the {@link Gpio} is not {@link Direction#OUT}, or if something goes wrong while
* writing to the file
*/
public void writeValue(Gpio gpio, Signal signal) throws GpioException {
if (gpio.getDirection() != Direction.OUT)
throw new IllegalArgumentException("For writing the direction must be " + Direction.OUT);
synchronized (gpio) {
if (gpio.getDirection() != Direction.OUT)
throw new GpioException("For writing the direction must be " + Direction.OUT);
File file = getGpioValuePath(gpio);
try (FileOutputStream out = new FileOutputStream(file)) {
File file = getGpioValuePath(gpio);
try (FileOutputStream out = new FileOutputStream(file)) {
out.write(signal.getValueS().getBytes());
out.flush();
out.write(signal.getValueS().getBytes());
out.flush();
gpio.setSignal(signal);
gpio.setSignal(signal);
} catch (Exception e) {
throw new GpioException("Failed to write GPIO " + gpio + " with signal " + signal, e);
} catch (Exception e) {
throw new GpioException("Failed to write GPIO " + gpio + " with signal " + signal, e);
}
System.out.println("Set GPIO " + gpio.getPin() + " signal to " + gpio.getSignal());
}
System.out.println("Set GPIO " + gpio.getPin() + " signal to " + gpio.getSignal());
}
/**
* <p>
* Public API method to read the current {@link Signal} on the given {@link Gpio}'s pin.
* </p>
*
* @param gpio
* the {@link Gpio} for which the {@link Signal} should be read
* @param gpio
*
* @return The {@link Gpio}'s current signal
*
* @throws GpioException
* if the direction of the {@link Gpio} is not {@link Direction#IN}, or if something goes wrong while
* reading from the file
*/
public Signal readValue(Gpio gpio) throws GpioException {
synchronized (gpio) {
if (gpio.getDirection() != Direction.IN)
throw new IllegalArgumentException("For reading the direction must be " + Direction.IN);
throw new GpioException("For reading the direction must be " + Direction.IN);
File file = getGpioValuePath(gpio);
try (BufferedReader fin = new BufferedReader(new FileReader(file))) {
@ -85,6 +154,10 @@ public class GpioBridge {
}
}
/**
* Starts the {@link GpioBridge}'s signal observing {@link Thread}. If no observers are registered with the
* {@link #register(Gpio, GpioSignalListener)}-method, then this method needs not to be called.
*/
public void start() {
this.run = true;
@ -156,6 +229,9 @@ public class GpioBridge {
System.out.println("Started GPIO bridge.");
}
/**
* Stops observing any pins and stops the {@link Thread}
*/
public void stop() {
this.run = false;
this.thread.interrupt();
@ -166,6 +242,29 @@ public class GpioBridge {
}
}
/**
* <p>
* Returns the {@link Gpio} for the given {@link Direction}.
* </p>
*
* <p>
* <b>Note:</b> This method can not be called multiple times with different {@link Direction}s. The
* {@link GpioBridge} does not handle pins that are simultaneously input and output as this is not supported by the
* Linux kernel.
* </p>
*
* @param pin
* The {@link Pin} for which the {@link Gpio} in the given {@link Direction} is to be returned
* @param direction
* the {@link Direction} for which this {@link Gpio} is to be returned
*
* @return The {@link Gpio} with the configured {@link Direction}
*
* @throws GpioException
* If the given {@link Direction} does not match the kernel's configured direction, or if the file
* permissions are not set so that the Java process can access the file (read access for input pin,
* write access for output pin.
*/
public synchronized Gpio getGpio(Pin pin, Direction direction) throws GpioException {
Gpio gpio = this.cache.get(pin);
if (gpio == null) {
@ -173,10 +272,10 @@ public class GpioBridge {
gpio = new Gpio(pin, direction);
// validate direction
validateDirection(pin, direction, gpio);
assertDirection(gpio);
// validate file permissions
validateFilePermissions(direction, gpio);
validateFilePermissions(gpio);
this.cache.put(pin, gpio);
System.out.println("Initialized pin " + pin + " with direction " + direction + ".");
@ -185,15 +284,24 @@ public class GpioBridge {
return gpio;
}
private void validateDirection(Pin pin, Direction direction, Gpio gpio) throws GpioException {
/**
* Validates the direction of the {@link Gpio} is the same as kernel's exported state
*
* @param gpio
* the {@link Gpio} been asserted for direction
*
* @throws GpioException
* if the assertion fails
*/
private void assertDirection(Gpio gpio) throws GpioException {
File file = getGpioDirectionPath(gpio);
Pin pin = gpio.getPin();
try (BufferedReader fin = new BufferedReader(new FileReader(file))) {
String directionS = fin.readLine();
Direction dir = Direction.getDirection(directionS);
if (dir != direction)
throw new GpioException(
"Actual direction of GPIO " + gpio.getPin() + " is " + dir + " not " + directionS);
if (dir != gpio.getDirection())
throw new GpioException("Actual direction of GPIO " + pin + " is " + dir + " not " + directionS);
} catch (FileNotFoundException e) {
throw new GpioException("GPIO " + pin + " does not exist, was the pin exported to user space?", e);
@ -202,8 +310,22 @@ public class GpioBridge {
}
}
private void validateFilePermissions(Direction direction, Gpio gpio) throws GpioException {
/**
* Validates the file permissions of the {@link Gpio} is correct for the {@link Gpio}'s {@link Direction}:
* <ul>
* <li>read access for input pin</li>
* <li>write access for output pin</li>
* </ul>
*
* @param gpio
* the {@link Gpio} been asserted for direction
*
* @throws GpioException
* if the assertion fails
*/
private void validateFilePermissions(Gpio gpio) throws GpioException {
File gpioValuePath = getGpioValuePath(gpio);
Direction direction = gpio.getDirection();
if (direction == Direction.IN) {
if (!gpioValuePath.canRead())
throw new GpioException("GPIO " + gpio + " has direction " + direction
@ -218,6 +340,17 @@ public class GpioBridge {
}
}
/**
* Registers the given {@link GpioSignalListener} for changes to {@link Signal}s on the given {@link Gpio}
*
* @param gpio
* the {@link Gpio} being observed
* @param listener
* the {@link GpioSignalListener} to be notified on changes on the {@link Gpio}'s {@link Signal}
*
* @throws GpioException
* if the {@link Direction} of the {@link Gpio} is not {@link Direction#IN}
*/
public void register(Gpio gpio, GpioSignalListener listener) throws GpioException {
if (gpio.getDirection() != Direction.IN)
@ -238,17 +371,27 @@ public class GpioBridge {
}
}
public void unregister(GpioBridgeTest gpio, GpioSignalListener listener) {
/**
* Unregisters a {@link GpioSignalListener} from changes to the given {@link Gpio}
*
* @param gpio
* the {@link Gpio} for which the listener is to be removed
* @param listener
* the {@link GpioSignalListener} to be removed from changes to the given {@link Gpio}
*/
public boolean unregister(GpioBridgeTest gpio, GpioSignalListener listener) {
synchronized (this.listeners) {
List<GpioSignalListener> listeners = this.listeners.get(gpio);
if (listeners == null) {
return;
return false;
}
listeners.remove(listener);
boolean removed = listeners.remove(listener);
if (listeners.isEmpty())
this.listeners.remove(gpio);
return removed;
}
}
}

View File

@ -2,8 +2,6 @@ package ch.eitchnet.beaglebone;
public class GpioBridgeTest {
static final String GPIO_PATH = "/sys/class/gpio/";
private static Gpio redBtn;
private static Gpio blueBtn;
private static Gpio greenBtn;

View File

@ -1,13 +1,32 @@
package ch.eitchnet.beaglebone;
/**
* General {@link Exception} for exceptional situations while using the {@link GpioBridge}
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class GpioException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Construct a {@link GpioException} with message and cause
*
* @param message
* the exception message
* @param cause
* the casue of the exception
*/
public GpioException(String message, Throwable cause) {
super(message, cause);
}
/**
* Construct a {@link GpioException} with message
*
* @param message
* the exception message
*/
public GpioException(String message) {
super(message);
}

View File

@ -1,5 +1,28 @@
package ch.eitchnet.beaglebone;
/**
* <p>
* Interface to define a listener for changes to a {@link Gpio}'s {@link Signal} when the {@link Direction} of the
* {@link Gpio} is {@link Direction#IN}
* </p>
*
* </p>
* Register the {@link GpioSignalListener} calling {@link GpioBridge#register(Gpio, GpioSignalListener)}
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public interface GpioSignalListener {
/**
* Notifies the {@link GpioSignalListener} when a change to the {@link Gpio} is detected
*
* @param gpio
* the {@link Gpio} for which the {@link Signal} changed. The actual signal is to be retrieved by calling
* {@link Gpio#getSignal()}
*
* @throws Exception
* if something goes wrong while handling the signal change
*/
public void notify(Gpio gpio) throws Exception;
}

View File

@ -1,5 +1,17 @@
package ch.eitchnet.beaglebone;
/**
* <p>
* This enum defines all the {@link Gpio} pins of the BeagleBoneBlack
* </p>
*
* <p>
* The BealgeBoneBlack has two banks of pins, of which not all pins can be used as a GPIO (general purpose
* input/outpunt) pin.
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public enum Pin {
P8_03("P8.03", 1, 6),
@ -83,22 +95,38 @@ public enum Pin {
this.pin = pin;
}
/**
* @return the user label of this pin, e.g. "P8.03"
*/
public String getLabel() {
return this.label;
}
/**
* @return the GPIO chip on which this Pin is configured on the BeagleBoneBlack's CPU
*/
public int getChip() {
return this.chip;
}
/**
* @return The kernel pin number. <b>Note:</b> this is not the pin number on the pin header, neither the GPIO number
* used to export the pin to userspace
*/
public int getPin() {
return this.pin;
}
/**
* @return The GPIO number with which the pin is exported to userspace
*/
public int getGpioNr() {
return this.chip * 32 + this.pin;
}
/**
* Returns the configured label e.g. "P8.03"
*/
@Override
public String toString() {
return this.label;

View File

@ -1,5 +1,21 @@
package ch.eitchnet.beaglebone;
/**
* <p>
* Defines the {@link Signal} which a {@link Gpio} can have
* </p>
*
* <p>
* A pin's signal is its current value. Depending on the {@link Gpio}'s {@link Direction}, the signal is an input value,
* or an output value.
* </p>
*
* <p>
* A signal of {@link #LOW} means that the value is 0, and a value of {@link #HIGH} means the value is 1
* </p>
*
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public enum Signal {
LOW(0, "0", false, "low"), HIGH(1, "1", true, "high");
@ -15,28 +31,51 @@ public enum Signal {
this.signal = signal;
}
/**
* @return the value which is either 0 or 1
*/
public int getValue() {
return this.value;
}
/**
* @return the value as a string which is either 0 or 1
*/
public String getValueS() {
return this.valueS;
}
/**
* @return true for a high signal, i.e. value of 1. Return false for a low value, i.e. a value of 0
*/
public boolean isHigh() {
return this.high;
}
/**
* @return true for a low signal, i.e. value of 0. Return false for a high value, i.e. a value of 1
*/
public String getSignal() {
return this.signal;
}
/**
* @return the opposite of the current {@link Signal}
*/
public Signal getOpposite() {
if (this.high)
return LOW;
return HIGH;
}
/**
* Returns the {@link Signal} enum for the given value
*
* @param value
* the value for which to return the {@link Signal}
*
* @return the {@link Signal} for the given value
*/
public static Signal getSignal(int value) {
if (value == 0)
return LOW;
@ -45,6 +84,14 @@ public enum Signal {
throw new IllegalArgumentException("No signal for value " + value);
}
/**
* Returns the {@link Signal} enum for the given string value
*
* @param value
* the value for which to return the {@link Signal}
*
* @return the {@link Signal} for the given string value
*/
public static Signal getSignal(String valueS) {
if (valueS.equals(LOW.valueS))
return LOW;
@ -52,9 +99,4 @@ public enum Signal {
return HIGH;
throw new IllegalArgumentException("No signal for value " + valueS);
}
@Override
public String toString() {
return name();
}
}