Skip to content

Commit

Permalink
Merge pull request #351 from Pi4J/feature/i2c-execute
Browse files Browse the repository at this point in the history
[Major] I2C extensions, fixed and cleanup
  • Loading branch information
FDelporte committed Apr 26, 2024
2 parents c0ef61a + a7ba4f0 commit 9c9927e
Show file tree
Hide file tree
Showing 15 changed files with 377 additions and 177 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.pi4j.plugin.linuxfs.provider.i2c;
package com.pi4j.common;

@FunctionalInterface
public interface CheckedFunction<T, R> {
Expand Down
75 changes: 58 additions & 17 deletions pi4j-core/src/main/java/com/pi4j/io/i2c/I2C.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,19 @@
import com.pi4j.io.IODataReader;
import com.pi4j.io.IODataWriter;

import java.util.concurrent.Callable;

/**
* I2C I/O Interface for Pi4J I2C Bus/Device Communications
*
* @author Robert Savage
*
* Based on previous contributions from:
* Daniel Sendula,
* <a href="http://raspelikan.blogspot.co.at">RasPelikan</a>
* <p>
* Based on previous contributions from: Daniel Sendula,
* <a href="http://raspelikan.blogspot.co.at">RasPelikan</a>
* @version $Id: $Id
*/
public interface I2C extends IO<I2C, I2CConfig, I2CProvider>,
IODataWriter,
IODataReader,
I2CRegisterDataReaderWriter,
AutoCloseable {
public interface I2C
extends IO<I2C, I2CConfig, I2CProvider>, IODataWriter, IODataReader, I2CRegisterDataReaderWriter, AutoCloseable {

/**
* <p>close.</p>
Expand All @@ -58,9 +55,10 @@ public interface I2C extends IO<I2C, I2CConfig, I2CProvider>,
* <p>newConfigBuilder.</p>
*
* @param context {@link Context}
*
* @return a {@link com.pi4j.io.i2c.I2CConfigBuilder} object.
*/
static I2CConfigBuilder newConfigBuilder(Context context){
static I2CConfigBuilder newConfigBuilder(Context context) {
return I2CConfigBuilder.newInstance(context);
}

Expand All @@ -69,7 +67,7 @@ static I2CConfigBuilder newConfigBuilder(Context context){
*
* @return The I2C device address for which this instance is constructed for.
*/
default int device(){
default int device() {
return config().device();
}

Expand All @@ -78,7 +76,7 @@ default int device(){
*
* @return The I2C bus address for which this instance is constructed for.
*/
default int bus(){
default int bus() {
return config().bus();
}

Expand All @@ -94,7 +92,7 @@ default int bus(){
*
* @return The I2C bus address for which this instance is constructed for.
*/
default int getBus(){
default int getBus() {
return bus();
}

Expand All @@ -103,26 +101,69 @@ default int getBus(){
*
* @return The I2C device address for which this instance is constructed for.
*/
default int getDevice(){
default int getDevice() {
return device();
}

/**
* Method to perform a write of the given buffer, and then a read into the given buffer
*
* @param writeBuffer the buffer to write
* @param readBuffer the buffer to read into
*
* @return the number of bytes read
*/
default int writeRead(byte[] writeBuffer, byte[] readBuffer) {
return writeRead(writeBuffer, writeBuffer.length, 0, readBuffer, readBuffer.length, 0);
}

/**
* Method to perform a write of the given buffer, and then a read into the given buffer
*
* @param writeSize the number of bytes to write
* @param writeOffset the offset of the array to write
* @param writeBuffer the buffer to write respecting the given length and offset
* @param readSize the number of bytes to read
* @param readOffset the offset in the read buffer at which to insert the read bytes
* @param readBuffer the buffer into which to read the bytes
*
* @return the number of bytes read
*/
default int writeRead(byte[] writeBuffer, int writeSize, int writeOffset, byte[] readBuffer, int readSize,
int readOffset) {
return execute(() -> {
int written = write(writeBuffer, writeOffset, writeSize);
if (written != writeOffset)
throw new IllegalStateException(
"Expected to write " + writeOffset + " bytes but only wrote " + written + " bytes");
return read(readBuffer, readOffset, readSize);
});
}

/**
* Get an encapsulated interface for reading and writing to a specific I2C device register
*
* @param address a int.
*
* @return a {@link com.pi4j.io.i2c.I2CRegister} object.
*/
I2CRegister getRegister(int address);

/**
* I2C Device Register
* Get an encapsulated interface for reading and writing to a specific I2C device register
* I2C Device Register Get an encapsulated interface for reading and writing to a specific I2C device register
*
* @param address the (16-bit) device register address
*
* @return an instance of I2CRegister for the provided register address
*/
default I2CRegister register(int address){
default I2CRegister register(int address) {
return getRegister(address);
}

/**
* Executes the given runnable on the I2C bus, locking the bus for the duration of the given task
*
* @param action the action to perform, returning a value
*/
<T> T execute(Callable<T> action);
}
44 changes: 31 additions & 13 deletions pi4j-core/src/main/java/com/pi4j/io/i2c/I2CBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,59 +30,77 @@
import com.pi4j.io.IOBase;
import com.pi4j.io.i2c.impl.DefaultI2CRegister;

import java.util.concurrent.Callable;

/**
* <p>Abstract I2CBase class.</p>
*
* @author Robert Savage (<a href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>)
* @version $Id: $Id
*/
public abstract class I2CBase extends IOBase<I2C, I2CConfig, I2CProvider> implements I2C {
public abstract class I2CBase<T extends I2CBus> extends IOBase<I2C, I2CConfig, I2CProvider> implements I2C {

protected boolean isOpen = false;
protected boolean isOpen;
protected final T i2CBus;

/**
* <p>Constructor for I2CBase.</p>
*
* @param provider a {@link com.pi4j.io.i2c.I2CProvider} object.
* @param config a {@link com.pi4j.io.i2c.I2CConfig} object.
* @param provider a {@link I2CProvider} object.
* @param config a {@link I2CConfig} object.
* @param i2CBus a {@link I2CBus} object.
*/
public I2CBase(I2CProvider provider, I2CConfig config) {
public I2CBase(I2CProvider provider, I2CConfig config, T i2CBus) {
super(provider, config);
this.isOpen = true;
this.i2CBus = i2CBus;
}

/** {@inheritDoc} */
/**
* {@inheritDoc}
*/
@Override
public boolean isOpen() {
return this.isOpen;
}

/** {@inheritDoc} */
/**
* {@inheritDoc}
*/
@Override
public void close() {
this.isOpen = false;
}

/**
* {@inheritDoc}
*
* <p>
* Get an encapsulated interface for reading and writing to a specific I2C device register
*/
public I2CRegister getRegister(int address){
public I2CRegister getRegister(int address) {
return new DefaultI2CRegister(this, address);
}

/** {@inheritDoc} */
@Override
public <V> V execute(Callable<V> action) {
if (action == null)
throw new NullPointerException("Parameter 'action' is mandatory!");
return this.i2CBus.execute(this, action);
}

/**
* {@inheritDoc}
*/
@Override
public I2C shutdown(Context context) throws ShutdownException {
// if this I2C device is still open, then we need to close it since we are shutting down
if(this.isOpen()) {
if (this.isOpen()) {
try {
this.close();
close();
} catch (Exception e) {
throw new ShutdownException(e);
}
}
return (I2C)this;
return this;
}
}
22 changes: 22 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/io/i2c/I2CBus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.pi4j.io.i2c;

import java.util.concurrent.Callable;

/**
* This interface defines method to be performed on an I2C bus. Most importantly the {@link #execute(I2C, Callable)}
* allows to perform bulk operations on the bus in a thread safe manner.
*/
public interface I2CBus {

/**
* Executes the given action, which typically performs multiple I2C reads and/or writes on the I2C bus in a thread
* safe manner, i.e. the bus is blocked till the action is completed.
*
* @param i2c the device for which to perform the action
* @param action the action to perform
* @param <R> the result type of the action, if any
*
* @return the result of the action
*/
<R> R execute(I2C i2c, Callable<R> action);
}
60 changes: 60 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/io/i2c/I2CBusBase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.pi4j.io.i2c;

import com.pi4j.exception.Pi4JException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import static java.text.MessageFormat.format;

public abstract class I2CBusBase implements I2CBus {

private static final Logger logger = LoggerFactory.getLogger(I2CBusBase.class);

public static final long DEFAULT_LOCK_ACQUIRE_TIMEOUT = 1000;
public static final TimeUnit DEFAULT_LOCK_ACQUIRE_TIMEOUT_UNITS = TimeUnit.MILLISECONDS;

protected final int bus;

protected final long lockAquireTimeout;
protected final TimeUnit lockAquireTimeoutUnit;
private final ReentrantLock lock = new ReentrantLock(true);

public I2CBusBase(I2CConfig config) {
if (config.bus() == null)
throw new IllegalArgumentException("I2C bus must be specified");

this.bus = config.getBus();

this.lockAquireTimeout = DEFAULT_LOCK_ACQUIRE_TIMEOUT;
this.lockAquireTimeoutUnit = DEFAULT_LOCK_ACQUIRE_TIMEOUT_UNITS;
}

protected <R> R _execute(I2C i2c, Callable<R> action) {
if (i2c == null)
throw new NullPointerException("Parameter 'i2c' is mandatory!");
if (action == null)
throw new NullPointerException("Parameter 'action' is mandatory!");
try {
if (this.lock.tryLock() || this.lock.tryLock(this.lockAquireTimeout, this.lockAquireTimeoutUnit)) {
try {
return action.call();
} finally {
this.lock.unlock();
}
} else {
throw new Pi4JException(
format("Failed to get I2C lock on bus {0} after {1} {2}", this.bus, this.lockAquireTimeout,
this.lockAquireTimeoutUnit));
}
} catch (InterruptedException e) {
logger.error("Failed locking {}-{}", getClass().getSimpleName(), this.bus, e);
throw new RuntimeException("Could not obtain an access-lock!", e);
} catch (Exception e) {
throw new Pi4JException("Failed to execute action for device " + i2c.device() + " on bus " + this.bus, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* @author Robert Savage (<a href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>)
* @version $Id: $Id
*/
public class LinuxFsI2C extends I2CBase implements I2C {
public class LinuxFsI2C extends I2CBase<LinuxFsI2CBus> implements I2C {

private final LinuxFsI2CBus i2CBus;

Expand All @@ -53,7 +53,7 @@ public class LinuxFsI2C extends I2CBase implements I2C {
* a {@link I2CConfig} object.
*/
public LinuxFsI2C(LinuxFsI2CBus i2CBus, I2CProvider provider, I2CConfig config) {
super(provider, config);
super(provider, config, i2CBus);
this.i2CBus = i2CBus;
}

Expand Down Expand Up @@ -332,11 +332,4 @@ public int writeReadRegisterWord(int register, int word) {
return word;

}

@Override
public void close() {
if (this.i2CBus != null)
this.i2CBus.close();
super.close();
}
}

0 comments on commit 9c9927e

Please sign in to comment.