[oceanic] Remove dependency on RXTX for serial communication (#15044)

Signed-off-by: Karel Goderis <karel.goderis@me.com>
This commit is contained in:
Karel Goderis 2023-06-02 21:57:32 +02:00 committed by GitHub
parent 3c1cbaa769
commit 93b888ad31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 79 deletions

View File

@ -14,8 +14,4 @@
<name>openHAB Add-ons :: Bundles :: Oceanic Binding</name> <name>openHAB Add-ons :: Bundles :: Oceanic Binding</name>
<properties>
<bnd.importpackage>gnu.io;version="[3.12,6)"</bnd.importpackage>
</properties>
</project> </project>

View File

@ -21,12 +21,15 @@ import java.util.stream.Stream;
import org.openhab.binding.oceanic.internal.handler.NetworkOceanicThingHandler; import org.openhab.binding.oceanic.internal.handler.NetworkOceanicThingHandler;
import org.openhab.binding.oceanic.internal.handler.SerialOceanicThingHandler; import org.openhab.binding.oceanic.internal.handler.SerialOceanicThingHandler;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory; import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/** /**
* The {@link OceanicHandlerFactory} is responsible for creating things and * The {@link OceanicHandlerFactory} is responsible for creating things and
@ -40,6 +43,13 @@ public class OceanicHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(THING_TYPE_SERIAL, THING_TYPE_NETWORK).collect(Collectors.toSet())); .unmodifiableSet(Stream.of(THING_TYPE_SERIAL, THING_TYPE_NETWORK).collect(Collectors.toSet()));
private final SerialPortManager serialPortManager;
@Activate
public OceanicHandlerFactory(final @Reference SerialPortManager serialPortManager) {
this.serialPortManager = serialPortManager;
}
@Override @Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) { public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
@ -50,7 +60,7 @@ public class OceanicHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_SERIAL)) { if (thingTypeUID.equals(THING_TYPE_SERIAL)) {
return new SerialOceanicThingHandler(thing); return new SerialOceanicThingHandler(thing, serialPortManager);
} }
if (thingTypeUID.equals(THING_TYPE_NETWORK)) { if (thingTypeUID.equals(THING_TYPE_NETWORK)) {
return new NetworkOceanicThingHandler(thing); return new NetworkOceanicThingHandler(thing);

View File

@ -63,10 +63,12 @@ public class NetworkOceanicThingHandler extends OceanicThingHandler {
try { try {
socket = new Socket(config.ipAddress, config.portNumber); socket = new Socket(config.ipAddress, config.portNumber);
if (socket != null) {
socket.setSoTimeout(REQUEST_TIMEOUT); socket.setSoTimeout(REQUEST_TIMEOUT);
outputStream = socket.getOutputStream(); outputStream = socket.getOutputStream();
inputStream = socket.getInputStream(); inputStream = socket.getInputStream();
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
}
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
logger.error("An exception occurred while resolving host {}:{} : '{}'", config.ipAddress, config.portNumber, logger.error("An exception occurred while resolving host {}:{} : '{}'", config.ipAddress, config.portNumber,
e.getMessage()); e.getMessage());

View File

@ -17,45 +17,47 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Enumeration; import java.util.TooManyListenersException;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openhab.binding.oceanic.internal.SerialOceanicBindingConfiguration; import org.openhab.binding.oceanic.internal.SerialOceanicBindingConfiguration;
import org.openhab.binding.oceanic.internal.Throttler; import org.openhab.binding.oceanic.internal.Throttler;
import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortEvent;
import org.openhab.core.io.transport.serial.SerialPortEventListener;
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusDetail;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.RXTXCommDriver;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
/** /**
* The {@link SerialOceanicThingHandler} implements {@link OceanicThingHandler} for an Oceanic water softener that is * The {@link SerialOceanicThingHandler} implements {@link OceanicThingHandler} for an Oceanic water softener that is
* directly connected to a serial port of the openHAB host * directly connected to a serial port of the openHAB host
* *
* @author Karel Goderis - Initial contribution * @author Karel Goderis - Initial contribution
*/ */
public class SerialOceanicThingHandler extends OceanicThingHandler { public class SerialOceanicThingHandler extends OceanicThingHandler implements SerialPortEventListener {
private static final long REQUEST_TIMEOUT = 10000; private static final long REQUEST_TIMEOUT = 10000;
private static final int BAUD = 19200; private static final int BAUD = 19200;
private final Logger logger = LoggerFactory.getLogger(SerialOceanicThingHandler.class); private final Logger logger = LoggerFactory.getLogger(SerialOceanicThingHandler.class);
private final SerialPortManager serialPortManager;
private SerialPort serialPort; private SerialPort serialPort;
private CommPortIdentifier portId;
private InputStream inputStream; private InputStream inputStream;
private OutputStream outputStream; private OutputStream outputStream;
private SerialPortReader readerThread; private SerialPortReader readerThread;
public SerialOceanicThingHandler(Thing thing) { public SerialOceanicThingHandler(Thing thing, SerialPortManager serialPortManager) {
super(thing); super(thing);
this.serialPortManager = serialPortManager;
} }
@Override @Override
@ -65,73 +67,46 @@ public class SerialOceanicThingHandler extends OceanicThingHandler {
SerialOceanicBindingConfiguration config = getConfigAs(SerialOceanicBindingConfiguration.class); SerialOceanicBindingConfiguration config = getConfigAs(SerialOceanicBindingConfiguration.class);
if (serialPort == null && config.port != null) { if (serialPort == null && config.port != null) {
if (portId == null) {
try {
RXTXCommDriver rxtxCommDriver = new RXTXCommDriver();
rxtxCommDriver.initialize();
CommPortIdentifier.addPortName(config.port, CommPortIdentifier.PORT_RAW, rxtxCommDriver);
portId = CommPortIdentifier.getPortIdentifier(config.port);
} catch (NoSuchPortException e) {
logger.error("An exception occurred while setting up serial port '{}' : '{}'", config.port,
e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Could not setup serial port " + serialPort + ": " + e.getMessage());
return;
}
}
if (portId != null) { SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(config.port);
try {
serialPort = portId.open(this.getThing().getUID().getBindingId(), 2000); if (portIdentifier == null) {
} catch (PortInUseException e) { String availablePorts = serialPortManager.getIdentifiers().map(id -> id.getName())
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, .collect(Collectors.joining(System.lineSeparator()));
"Could not open serial port " + serialPort + ": " + e.getMessage()); String description = String.format("Serial port '%s' could not be found. Available ports are:%n%s",
config.port, availablePorts);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, description);
return; return;
} }
try { try {
inputStream = serialPort.getInputStream(); logger.info("Connecting to the Oceanic water softener using {}.", config.port);
} catch (IOException e) { serialPort = portIdentifier.open(this.getThing().getUID().getBindingId(), 2000);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Could not open serial port " + serialPort + ": " + e.getMessage());
return;
}
serialPort.notifyOnDataAvailable(true);
try {
serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE); SerialPort.PARITY_NONE);
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
} catch (UnsupportedCommOperationException e) { serialPort.enableReceiveThreshold(1);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, serialPort.disableReceiveTimeout();
"Could not configure serial port " + serialPort + ": " + e.getMessage());
return;
}
try { inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream(); outputStream = serialPort.getOutputStream();
updateStatus(ThingStatus.ONLINE);
} catch (IOException e) { serialPort.addEventListener(this);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, serialPort.notifyOnDataAvailable(true);
"Could not communicate with the serial port " + serialPort + ": " + e.getMessage());
return;
}
readerThread = new SerialPortReader(inputStream); readerThread = new SerialPortReader(inputStream);
readerThread.start(); readerThread.start();
} else {
StringBuilder sb = new StringBuilder(); updateStatus(ThingStatus.ONLINE);
@SuppressWarnings("rawtypes")
Enumeration portList = CommPortIdentifier.getPortIdentifiers(); } catch (PortInUseException portInUseException) {
while (portList.hasMoreElements()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Port in use: " + config.port);
CommPortIdentifier id = (CommPortIdentifier) portList.nextElement(); } catch (UnsupportedCommOperationException | IOException e) {
if (id.getPortType() == CommPortIdentifier.PORT_SERIAL) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error");
sb.append(id.getName() + "\n"); } catch (TooManyListenersException e) {
} updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
} "Too many listeners to serial port.");
logger.error("Serial port '{}' could not be found. Available ports are:\n {}", config.port, sb);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
} }
} }
} }
@ -217,6 +192,13 @@ public class SerialOceanicThingHandler extends OceanicThingHandler {
} }
} }
@Override
public void serialEvent(SerialPortEvent serialPortEvent) {
if (logger.isTraceEnabled()) {
logger.trace("Received a serial port event : {}", serialPortEvent.getEventType());
}
}
public class SerialPortReader extends Thread { public class SerialPortReader extends Thread {
private boolean interrupted = false; private boolean interrupted = false;