Skip to content

Commit 4b92763

Browse files
committed
#454 Implemented DigitalOutput, some unit tests, initial JMH Performance testing
1 parent 4be0231 commit 4b92763

File tree

9 files changed

+255
-67
lines changed

9 files changed

+255
-67
lines changed

plugins/pi4j-plugin-ffm/pom.xml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
<version>${slf4j.version}</version>
3535
<scope>test</scope>
3636
</dependency>
37+
<dependency>
38+
<groupId>org.openjdk.jmh</groupId>
39+
<artifactId>jmh-core</artifactId>
40+
<version>1.37</version>
41+
<scope>test</scope>
42+
</dependency>
3743
</dependencies>
3844
<!-- STANDARD BUILD INSTRUCTIONS -->
3945
<build>
@@ -48,9 +54,14 @@
4854
<target>24</target>
4955
<annotationProcessorPaths>
5056
<annotationProcessorPath>
51-
<groupId>io.github.digitalsmile.native</groupId>
52-
<artifactId>annotation-processor</artifactId>
53-
<version>1.1.5</version>
57+
<groupId>io.github.digitalsmile.native</groupId>
58+
<artifactId>annotation-processor</artifactId>
59+
<version>1.1.5</version>
60+
</annotationProcessorPath>
61+
<annotationProcessorPath>
62+
<groupId>org.openjdk.jmh</groupId>
63+
<artifactId>jmh-generator-annprocess</artifactId>
64+
<version>1.37</version>
5465
</annotationProcessorPath>
5566
</annotationProcessorPaths>
5667
</configuration>
@@ -145,6 +156,11 @@
145156
</execution>
146157
</executions>
147158
</plugin>
159+
<plugin>
160+
<groupId>pw.krejci</groupId>
161+
<artifactId>jmh-maven-plugin</artifactId>
162+
<version>0.2.2</version>
163+
</plugin>
148164
</plugins>
149165
</build>
150166

plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/FFMPlugin.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import com.pi4j.exception.ShutdownException;
55
import com.pi4j.extension.Plugin;
66
import com.pi4j.extension.PluginService;
7-
import com.pi4j.plugin.ffm.providers.gpio.PinInputProviderImpl;
8-
import com.pi4j.plugin.ffm.providers.gpio.PinOutputProviderImpl;
7+
import com.pi4j.plugin.ffm.providers.gpio.DigitalInputFFMProviderImpl;
8+
import com.pi4j.plugin.ffm.providers.gpio.DigitalOutputFFMProviderImpl;
99
import com.pi4j.plugin.ffm.providers.i2c.FFMI2CProviderImpl;
1010
import com.pi4j.plugin.ffm.providers.pwm.FFMPwmProviderImpl;
1111
import com.pi4j.plugin.ffm.providers.spi.FFMSpiProviderImpl;
@@ -19,8 +19,8 @@ public class FFMPlugin implements Plugin {
1919
@Override
2020
public void initialize(PluginService service) {
2121
this.providers = new Provider[]{
22-
new PinInputProviderImpl(),
23-
new PinOutputProviderImpl(),
22+
new DigitalInputFFMProviderImpl(),
23+
new DigitalOutputFFMProviderImpl(),
2424
new FFMI2CProviderImpl(),
2525
new FFMSpiProviderImpl(),
2626
new FFMPwmProviderImpl()

plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/providers/gpio/PinInput.java renamed to plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/providers/gpio/DigitalInputFFM.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
import java.util.List;
3838
import java.util.concurrent.*;
3939

40-
public class PinInput extends DigitalInputBase implements DigitalInput {
41-
private static final Logger logger = LoggerFactory.getLogger(PinInput.class);
40+
public class DigitalInputFFM extends DigitalInputBase implements DigitalInput {
41+
private static final Logger logger = LoggerFactory.getLogger(DigitalInputFFM.class);
4242
private static final Ioctl ioctl = new IoctlNative();
4343
private static final FileDescriptor file = new FileDescriptorNative();
4444
private static final Poll poll = new PollNative();
@@ -47,15 +47,15 @@ public class PinInput extends DigitalInputBase implements DigitalInput {
4747
private final int pin;
4848
private final long debounce;
4949
private final PullResistance pull;
50-
private int fd;
50+
private int chipFileDescriptor;
5151

5252
private static final ThreadFactory factory = Thread.ofVirtual().name("pin-input-event-detection-", 0).factory();
5353
// executor services for event watcher
5454
private static final ExecutorService eventTaskProcessor = Executors.newSingleThreadExecutor(factory);
5555

5656
private boolean closed = false;
5757

58-
public PinInput(String chipName, DigitalInputProvider provider, DigitalInputConfig config) {
58+
public DigitalInputFFM(String chipName, DigitalInputProvider provider, DigitalInputConfig config) {
5959
super(provider, config);
6060
this.pin = config.address();
6161
this.chipName = "/dev/" + chipName;
@@ -75,7 +75,7 @@ public DigitalInput initialize(Context context) throws InitializeException {
7575
logger.error("Please, read the documentation <link> to setup right permissions.");
7676
throw new InitializeException("Device '" + chipName + "' cannot be accessed with current user.");
7777
}
78-
logger.info("{}-{} - setting up GPIO Pin...", chipName, pin);
78+
logger.info("{}-{} - setting up DigitalInput Pin...", chipName, pin);
7979
logger.trace("{}-{} - opening device file.", chipName, pin);
8080
var fd = file.open(chipName, FileFlag.O_RDONLY | FileFlag.O_CLOEXEC);
8181
var lineInfo = new LineInfo(new byte[]{}, new byte[]{}, pin, 0, 0, new LineAttribute[]{}, new int[]{});
@@ -85,7 +85,7 @@ public DigitalInput initialize(Context context) throws InitializeException {
8585
shutdown(context());
8686
throw new InitializeException("Pin " + pin + " is in use");
8787
}
88-
logger.trace("{}-{} - GPIO Pin line info: {}", chipName, pin, lineInfo);
88+
logger.trace("{}-{} - DigitalInput Pin line info: {}", chipName, pin, lineInfo);
8989
var flags = PinFlag.INPUT.getValue() | PinFlag.EDGE_RISING.getValue() | PinFlag.EDGE_FALLING.getValue();
9090
var attributes = new ArrayList<LineConfigAttribute>();
9191
if (debounce > 0) {
@@ -98,9 +98,9 @@ public DigitalInput initialize(Context context) throws InitializeException {
9898
case PULL_UP -> PinFlag.BIAS_PULL_UP.getValue();
9999
};
100100
var lineConfig = new LineConfig(flags, attributes.size(), new int[]{}, attributes.toArray(new LineConfigAttribute[0]));
101-
var lineRequest = new LineRequest(new int[]{pin}, "pi4j FFM".getBytes(), lineConfig, 1, 0, new int[]{}, 0);
101+
var lineRequest = new LineRequest(new int[]{pin}, ("pi4j." + getClass().getSimpleName()).getBytes(), lineConfig, 1, 0, new int[]{}, 0);
102102
var result = ioctl.call(fd, Command.getGpioV2GetLineIoctl(), lineRequest);
103-
this.fd = result.fd();
103+
this.chipFileDescriptor = result.fd();
104104

105105
var watcher = new EventWatcher(fd, PinEvent.BOTH, events -> {
106106
for (DetectedEvent detectedEvent : events) {
@@ -109,9 +109,10 @@ public DigitalInput initialize(Context context) throws InitializeException {
109109
}, 16);
110110
eventTaskProcessor.submit(watcher);
111111

112-
logger.info("{}-{} - GPIO Pin configured: {}", chipName, pin, result);
112+
file.close(fd);
113+
logger.info("{}-{} - DigitalInput Pin configured: {}", chipName, pin, result);
113114
} catch (NativeMemoryException | IOException e) {
114-
logger.error("{}-{} - GPIO Pin Initialization error: {}", chipName, pin, e.getMessage());
115+
logger.error("{}-{} - DigitalInput Pin Initialization error: {}", chipName, pin, e.getMessage());
115116
throw new InitializeException(e);
116117
}
117118
return this;
@@ -122,8 +123,8 @@ public DigitalInput shutdown(Context context) throws ShutdownException {
122123
super.shutdown(context);
123124
logger.info("{}-{} - closing GPIO Pin.", chipName, pin);
124125
try {
125-
if (fd > 0) {
126-
file.close(fd);
126+
if (chipFileDescriptor > 0) {
127+
file.close(chipFileDescriptor);
127128
}
128129
eventTaskProcessor.awaitTermination(1, TimeUnit.MILLISECONDS);
129130
} catch (NativeMemoryException | InterruptedException e) {
@@ -142,12 +143,12 @@ public DigitalState state() {
142143
var lineValues = new LineValues(0, 1);
143144
LineValues result;
144145
try {
145-
result = ioctl.call(fd, Command.getGpioV2GetValuesIoctl(), lineValues);
146+
result = ioctl.call(chipFileDescriptor, Command.getGpioV2GetValuesIoctl(), lineValues);
146147
} catch (NativeMemoryException e) {
147148
throw new RuntimeException(e);
148149
}
149150
var state = DigitalState.getState(result.bits());
150-
logger.trace("{}-{} - new GPIO Pin state is {}.", chipName, pin, state);
151+
logger.trace("{}-{} - GPIO Pin state is {}.", chipName, pin, state);
151152
return state;
152153
}
153154

plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/providers/gpio/PinInputProviderImpl.java renamed to plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/providers/gpio/DigitalInputFFMProviderImpl.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,17 @@
88
import com.pi4j.io.gpio.digital.DigitalInputProvider;
99
import com.pi4j.io.gpio.digital.DigitalInputProviderBase;
1010

11-
public class PinInputProviderImpl extends DigitalInputProviderBase implements DigitalInputProvider {
11+
public class DigitalInputFFMProviderImpl extends DigitalInputProviderBase implements DigitalInputProvider {
1212

13-
public PinInputProviderImpl() {
14-
this.id = "PinOutputProviderFFM";
13+
public DigitalInputFFMProviderImpl() {
14+
this.id = getClass().getSimpleName();
1515
this.name = "Digital Pin Input (FFM API)";
1616
}
1717

1818
@Override
1919
public DigitalInput create(DigitalInputConfig config) {
20-
// create new I/O instance based on I/O config
21-
// GpioLine line = GpioDContext.getInstance().getOrOpenLine(config.address());
2220
var chipName = context.config().properties().get("gpio.chip.name");
23-
var digitalInput = new PinInput(chipName, this, config);
21+
var digitalInput = new DigitalInputFFM(chipName, this, config);
2422
this.context.registry().add(digitalInput);
2523
return digitalInput;
2624
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.pi4j.plugin.ffm.providers.gpio;
2+
3+
import com.pi4j.context.Context;
4+
import com.pi4j.exception.InitializeException;
5+
import com.pi4j.exception.ShutdownException;
6+
import com.pi4j.io.exception.IOException;
7+
import com.pi4j.io.gpio.digital.*;
8+
import com.pi4j.plugin.ffm.common.file.FileDescriptor;
9+
import com.pi4j.plugin.ffm.common.file.FileDescriptorNative;
10+
import com.pi4j.plugin.ffm.common.file.FileFlag;
11+
import com.pi4j.plugin.ffm.common.gpio.PinFlag;
12+
import com.pi4j.plugin.ffm.common.gpio.structs.*;
13+
import com.pi4j.plugin.ffm.common.ioctl.Command;
14+
import com.pi4j.plugin.ffm.common.ioctl.Ioctl;
15+
import com.pi4j.plugin.ffm.common.ioctl.IoctlNative;
16+
import io.github.digitalsmile.annotation.NativeMemoryException;
17+
import org.slf4j.Logger;
18+
import org.slf4j.LoggerFactory;
19+
20+
import java.io.File;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.nio.file.attribute.PosixFileAttributes;
24+
import java.nio.file.attribute.PosixFilePermissions;
25+
26+
public class DigitalOutputFFM extends DigitalOutputBase implements DigitalOutput {
27+
private static final Logger logger = LoggerFactory.getLogger(DigitalOutputFFM.class);
28+
private static final Ioctl ioctl = new IoctlNative();
29+
private static final FileDescriptor file = new FileDescriptorNative();
30+
31+
private final String chipName;
32+
private final int pin;
33+
private int chipFileDescriptor;
34+
private boolean closed = false;
35+
36+
public DigitalOutputFFM(String chipName, DigitalOutputProvider provider, DigitalOutputConfig config) {
37+
super(provider, config);
38+
this.pin = config.address();
39+
this.chipName = "/dev/" + chipName;
40+
}
41+
42+
@Override
43+
public DigitalOutput initialize(Context context) throws InitializeException {
44+
super.initialize(context);
45+
try {
46+
if (!deviceExists()) {
47+
throw new InitializeException("Device '" + chipName + "' does not exist.");
48+
}
49+
if (!canAccessDevice()) {
50+
var posix = Files.readAttributes(Path.of(chipName), PosixFileAttributes.class);
51+
logger.error("Inaccessible device: '{} {} {} {}'", PosixFilePermissions.toString(posix.permissions()), posix.owner().getName(), posix.group().getName(), chipName);
52+
logger.error("Please, read the documentation <link> to setup right permissions.");
53+
throw new InitializeException("Device '" + chipName + "' cannot be accessed with current user.");
54+
}
55+
logger.info("{}-{} - setting up DigitalOutput Pin...", chipName, pin);
56+
logger.trace("{}-{} - opening device file.", chipName, pin);
57+
var fd = file.open(chipName, FileFlag.O_RDONLY | FileFlag.O_CLOEXEC);
58+
var lineInfo = new LineInfo(new byte[]{}, new byte[]{}, pin, 0, 0, new LineAttribute[]{}, new int[]{});
59+
logger.trace("{}-{} - getting line info.", chipName, pin);
60+
lineInfo = ioctl.call(fd, Command.getGpioV2GetLineInfoIoctl(), lineInfo);
61+
if ((lineInfo.flags() & PinFlag.USED.getValue()) > 0) {
62+
shutdown(context());
63+
throw new InitializeException("Pin " + pin + " is in use");
64+
}
65+
logger.trace("{}-{} - DigitalOutput Pin line info: {}", chipName, pin, lineInfo);
66+
var flags = PinFlag.OUTPUT.getValue();
67+
var lineConfig = new LineConfig(flags, 0, new int[]{}, new LineConfigAttribute[]{});
68+
var lineRequest = new LineRequest(new int[]{pin}, ("pi4j." + getClass().getSimpleName()).getBytes(), lineConfig, 1, 0, new int[]{}, 0);
69+
var result = ioctl.call(fd, Command.getGpioV2GetLineIoctl(), lineRequest);
70+
this.chipFileDescriptor = result.fd();
71+
72+
file.close(fd);
73+
logger.info("{}-{} - DigitalOutput Pin configured: {}", chipName, pin, result);
74+
} catch (NativeMemoryException | java.io.IOException e) {
75+
logger.error("{}-{} - DigitalOutput Pin Initialization error: {}", chipName, pin, e.getMessage());
76+
throw new InitializeException(e);
77+
}
78+
return this;
79+
}
80+
81+
@Override
82+
public DigitalOutput shutdown(Context context) throws ShutdownException {
83+
super.shutdown(context);
84+
logger.info("{}-{} - closing GPIO Pin.", chipName, pin);
85+
try {
86+
if (chipFileDescriptor > 0) {
87+
file.close(chipFileDescriptor);
88+
}
89+
} catch (NativeMemoryException e) {
90+
this.closed = true;
91+
throw new ShutdownException(e);
92+
}
93+
this.closed = true;
94+
logger.info("{}-{} - GPIO Pin is closed. Recreate the pin object to reuse.", chipName, pin);
95+
return this;
96+
}
97+
98+
@Override
99+
public DigitalOutput state(DigitalState state) throws IOException {
100+
checkClosed();
101+
logger.trace("{}-{} - writing GPIO Pin {}.", chipName, pin, state);
102+
var lineValues = new LineValues(state.getValue().intValue(), 1);
103+
try {
104+
ioctl.call(chipFileDescriptor, Command.getGpioV2SetValuesIoctl(), lineValues);
105+
} catch (NativeMemoryException e) {
106+
throw new IOException(e);
107+
}
108+
return super.state(state);
109+
}
110+
111+
/**
112+
* Checks if GPIO Pin is closed.
113+
*/
114+
private void checkClosed() {
115+
if (closed) {
116+
throw new RuntimeException("Pin " + pin + " is closed");
117+
}
118+
}
119+
120+
private boolean canAccessDevice() {
121+
return new File(chipName).canRead();
122+
}
123+
124+
private boolean deviceExists() {
125+
return new File(chipName).exists();
126+
}
127+
}

plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/providers/gpio/PinOutputProviderImpl.java renamed to plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/providers/gpio/DigitalOutputFFMProviderImpl.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88
import com.pi4j.io.gpio.digital.DigitalOutputProvider;
99
import com.pi4j.io.gpio.digital.DigitalOutputProviderBase;
1010

11-
public class PinOutputProviderImpl extends DigitalOutputProviderBase implements DigitalOutputProvider {
11+
public class DigitalOutputFFMProviderImpl extends DigitalOutputProviderBase implements DigitalOutputProvider {
1212

13-
public PinOutputProviderImpl() {
14-
this.id = "PinOutputProviderFFM";
13+
public DigitalOutputFFMProviderImpl() {
14+
this.id = getClass().getSimpleName();
1515
this.name = "Digital Pin Output (FFM API)";
1616
}
1717

1818

1919
@Override
2020
public DigitalOutput create(DigitalOutputConfig config) {
21-
var digitalOutput = new PinOutput(this, config);
21+
var chipName = context.config().properties().get("gpio.chip.name");
22+
var digitalOutput = new DigitalOutputFFM(chipName, this, config);
2223
this.context.registry().add(digitalOutput);
2324
return digitalOutput;
2425
}

plugins/pi4j-plugin-ffm/src/main/java/com/pi4j/plugin/ffm/providers/gpio/PinOutput.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)