Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix serial ports missing when ports are added to system property #866

Merged
merged 1 commit into from Jun 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -13,12 +13,11 @@
package org.eclipse.smarthome.io.transport.serial.internal;

import java.net.URI;
import java.util.Enumeration;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
Expand All @@ -31,11 +30,12 @@
import org.slf4j.LoggerFactory;

import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;

/**
*
* @author Matthias Steigenberger - Initial Contribution
*
* @author Wouter Born - Fix serial ports missing when ports are added to system property
*/
@NonNullByDefault
@Component
Expand All @@ -45,18 +45,13 @@ public class RxTxPortProvider implements SerialPortProvider {

@Override
public @Nullable SerialPortIdentifier getPortIdentifier(URI port) {
CommPortIdentifier ident = null;
if ((System.getProperty("os.name").toLowerCase().indexOf("linux") != -1)) {
SerialPortUtil.appendSerialPortProperty(port.getPath());
}
try {
ident = CommPortIdentifier.getPortIdentifier(port.getPath());
} catch (gnu.io.NoSuchPortException e) {
CommPortIdentifier ident = SerialPortUtil.getPortIdentifier(port.getPath());
return new SerialPortIdentifierImpl(ident);
} catch (NoSuchPortException e) {
logger.debug("No SerialPortIdentifier found for: {}", port.getPath());
return null;
}
return new SerialPortIdentifierImpl(ident);

}

@Override
Expand All @@ -66,37 +61,17 @@ public Stream<ProtocolType> getAcceptedProtocols() {

@Override
public Stream<SerialPortIdentifier> getSerialPortIdentifiers() {
@SuppressWarnings("unchecked")
final Enumeration<CommPortIdentifier> ids = CommPortIdentifier.getPortIdentifiers();
return StreamSupport.stream(new SplitIteratorForEnumeration<>(ids), false)
Stream<CommPortIdentifier> scanIds = SerialPortUtil.getPortIdentifiersUsingScan();
Stream<CommPortIdentifier> propIds = SerialPortUtil.getPortIdentifiersUsingProperty();

return Stream.concat(scanIds, propIds).filter(distinctByKey(CommPortIdentifier::getName))
.filter(id -> id.getPortType() == CommPortIdentifier.PORT_SERIAL)
.map(sid -> new SerialPortIdentifierImpl(sid));
}

private static class SplitIteratorForEnumeration<T> extends Spliterators.AbstractSpliterator<T> {
private final Enumeration<T> e;

public SplitIteratorForEnumeration(final Enumeration<T> e) {
super(Long.MAX_VALUE, Spliterator.ORDERED);
this.e = e;
}

@Override
@NonNullByDefault({})
public boolean tryAdvance(Consumer<? super T> action) {
if (e.hasMoreElements()) {
action.accept(e.nextElement());
return true;
}
return false;
}

@Override
@NonNullByDefault({})
public void forEachRemaining(Consumer<? super T> action) {
while (e.hasMoreElements()) {
action.accept(e.nextElement());
}
}
@SuppressWarnings("null")
private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, String> seen = new ConcurrentHashMap<>();
return t -> seen.put(keyExtractor.apply(t), "") == null;
}
}
Expand Up @@ -13,6 +13,8 @@
package org.eclipse.smarthome.io.transport.serial.internal;

import java.io.File;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -21,30 +23,86 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;

/**
*
* @author Matthias Steigenberger - Initial Contribution
*
* @author Wouter Born - Fix serial ports missing when ports are added to system property
*/
@NonNullByDefault
public class SerialPortUtil {

private static final String GNU_IO_RXTX_SERIAL_PORTS = "gnu.io.rxtx.SerialPorts";

private synchronized static boolean isSerialPortsKeySet() {
return System.getProperties().containsKey(GNU_IO_RXTX_SERIAL_PORTS);
}

public synchronized static CommPortIdentifier getPortIdentifier(String port) throws NoSuchPortException {
if ((System.getProperty("os.name").toLowerCase().indexOf("linux") != -1)) {
appendSerialPortProperty(port);
}

return CommPortIdentifier.getPortIdentifier(port);
}

/**
* Registers the given port as system property {@value #GNU_IO_RXTX_SERIAL_PORTS}. The method is capable of
* extending the system property, if any other ports are already registered.
* Registers the given port as system property {@value #GNU_IO_RXTX_SERIAL_PORTS}.
* The method is capable of extending the system property, if any other ports are already registered.
*
* @param port the port to be registered
*/
public synchronized static void appendSerialPortProperty(String port) {
private synchronized static void appendSerialPortProperty(String port) {
String serialPortsProperty = System.getProperty(GNU_IO_RXTX_SERIAL_PORTS);
String newValue = initSerialPort(port, serialPortsProperty);
if (newValue != null) {
System.setProperty(GNU_IO_RXTX_SERIAL_PORTS, newValue);
}
}

/**
* Scans for available port identifiers by calling RXTX without using the ({@value #GNU_IO_RXTX_SERIAL_PORTS}
* property. Finds port identifiers based on operating system and distribution.
*
* @return the scanned port identifiers
*/
@SuppressWarnings("unchecked")
public synchronized static Stream<CommPortIdentifier> getPortIdentifiersUsingScan() {
Enumeration<CommPortIdentifier> identifiers;
if (isSerialPortsKeySet()) {
// Save the existing serial ports property
String value = System.getProperty(GNU_IO_RXTX_SERIAL_PORTS);
// Clear the property so library scans the ports
System.clearProperty(GNU_IO_RXTX_SERIAL_PORTS);
identifiers = CommPortIdentifier.getPortIdentifiers();
// Restore the existing serial ports property
System.setProperty(GNU_IO_RXTX_SERIAL_PORTS, value);
} else {
identifiers = CommPortIdentifier.getPortIdentifiers();
}

// Save the Enumeration to a new list so the result is thread safe
return Collections.list(identifiers).stream();
}

/**
* Get the port identifiers for the ports in the system property by calling RXTX while using the
* ({@value #GNU_IO_RXTX_SERIAL_PORTS} property.
*
* @return the port identifiers for the ports defined in the {@value #GNU_IO_RXTX_SERIAL_PORTS} property
*/
@SuppressWarnings("unchecked")
public synchronized static Stream<CommPortIdentifier> getPortIdentifiersUsingProperty() {
if (isSerialPortsKeySet()) {
// Save the Enumeration to a new list so the result is thread safe
return Collections.list(CommPortIdentifier.getPortIdentifiers()).stream();
}

return Stream.empty();
}

static @Nullable String initSerialPort(String port, @Nullable String serialPortsProperty) {

String pathSeparator = File.pathSeparator;
Expand Down