From 93b888ad31bb97caa30adb0bd0218050d5773959 Mon Sep 17 00:00:00 2001 From: Karel Goderis Date: Fri, 2 Jun 2023 21:57:32 +0200 Subject: [PATCH] [oceanic] Remove dependency on RXTX for serial communication (#15044) Signed-off-by: Karel Goderis --- bundles/org.openhab.binding.oceanic/pom.xml | 4 - .../internal/OceanicHandlerFactory.java | 12 +- .../handler/NetworkOceanicThingHandler.java | 10 +- .../handler/SerialOceanicThingHandler.java | 122 ++++++++---------- 4 files changed, 69 insertions(+), 79 deletions(-) diff --git a/bundles/org.openhab.binding.oceanic/pom.xml b/bundles/org.openhab.binding.oceanic/pom.xml index da59973c9..de8af3662 100644 --- a/bundles/org.openhab.binding.oceanic/pom.xml +++ b/bundles/org.openhab.binding.oceanic/pom.xml @@ -14,8 +14,4 @@ openHAB Add-ons :: Bundles :: Oceanic Binding - - gnu.io;version="[3.12,6)" - - diff --git a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/OceanicHandlerFactory.java b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/OceanicHandlerFactory.java index 6e4dfee7f..7496b00bc 100644 --- a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/OceanicHandlerFactory.java +++ b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/OceanicHandlerFactory.java @@ -21,12 +21,15 @@ import java.util.stream.Stream; import org.openhab.binding.oceanic.internal.handler.NetworkOceanicThingHandler; 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.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.ThingHandler; 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.Reference; /** * The {@link OceanicHandlerFactory} is responsible for creating things and @@ -40,6 +43,13 @@ public class OceanicHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Collections .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 public boolean supportsThingType(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); @@ -50,7 +60,7 @@ public class OceanicHandlerFactory extends BaseThingHandlerFactory { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(THING_TYPE_SERIAL)) { - return new SerialOceanicThingHandler(thing); + return new SerialOceanicThingHandler(thing, serialPortManager); } if (thingTypeUID.equals(THING_TYPE_NETWORK)) { return new NetworkOceanicThingHandler(thing); diff --git a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java index 1722a082f..a2def1158 100644 --- a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java +++ b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/NetworkOceanicThingHandler.java @@ -63,10 +63,12 @@ public class NetworkOceanicThingHandler extends OceanicThingHandler { try { socket = new Socket(config.ipAddress, config.portNumber); - socket.setSoTimeout(REQUEST_TIMEOUT); - outputStream = socket.getOutputStream(); - inputStream = socket.getInputStream(); - updateStatus(ThingStatus.ONLINE); + if (socket != null) { + socket.setSoTimeout(REQUEST_TIMEOUT); + outputStream = socket.getOutputStream(); + inputStream = socket.getInputStream(); + updateStatus(ThingStatus.ONLINE); + } } catch (UnknownHostException e) { logger.error("An exception occurred while resolving host {}:{} : '{}'", config.ipAddress, config.portNumber, e.getMessage()); diff --git a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java index b2444bcbc..125747e2b 100644 --- a/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java +++ b/bundles/org.openhab.binding.oceanic/src/main/java/org/openhab/binding/oceanic/internal/handler/SerialOceanicThingHandler.java @@ -17,45 +17,47 @@ import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; 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.openhab.binding.oceanic.internal.SerialOceanicBindingConfiguration; 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.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.slf4j.Logger; 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 * directly connected to a serial port of the openHAB host * * @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 int BAUD = 19200; private final Logger logger = LoggerFactory.getLogger(SerialOceanicThingHandler.class); + private final SerialPortManager serialPortManager; private SerialPort serialPort; - private CommPortIdentifier portId; private InputStream inputStream; private OutputStream outputStream; private SerialPortReader readerThread; - public SerialOceanicThingHandler(Thing thing) { + public SerialOceanicThingHandler(Thing thing, SerialPortManager serialPortManager) { super(thing); + this.serialPortManager = serialPortManager; } @Override @@ -65,73 +67,46 @@ public class SerialOceanicThingHandler extends OceanicThingHandler { SerialOceanicBindingConfiguration config = getConfigAs(SerialOceanicBindingConfiguration.class); 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; - } + + SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(config.port); + + if (portIdentifier == null) { + String availablePorts = serialPortManager.getIdentifiers().map(id -> id.getName()) + .collect(Collectors.joining(System.lineSeparator())); + 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; } - if (portId != null) { - try { - serialPort = portId.open(this.getThing().getUID().getBindingId(), 2000); - } catch (PortInUseException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Could not open serial port " + serialPort + ": " + e.getMessage()); - return; - } + try { + logger.info("Connecting to the Oceanic water softener using {}.", config.port); + serialPort = portIdentifier.open(this.getThing().getUID().getBindingId(), 2000); - try { - inputStream = serialPort.getInputStream(); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Could not open serial port " + serialPort + ": " + e.getMessage()); - return; - } + serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, + SerialPort.PARITY_NONE); + serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); + serialPort.enableReceiveThreshold(1); + serialPort.disableReceiveTimeout(); + inputStream = serialPort.getInputStream(); + outputStream = serialPort.getOutputStream(); + + serialPort.addEventListener(this); serialPort.notifyOnDataAvailable(true); - try { - serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, - SerialPort.PARITY_NONE); - serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); - } catch (UnsupportedCommOperationException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Could not configure serial port " + serialPort + ": " + e.getMessage()); - return; - } - - try { - outputStream = serialPort.getOutputStream(); - updateStatus(ThingStatus.ONLINE); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Could not communicate with the serial port " + serialPort + ": " + e.getMessage()); - return; - } - readerThread = new SerialPortReader(inputStream); readerThread.start(); - } else { - StringBuilder sb = new StringBuilder(); - @SuppressWarnings("rawtypes") - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - while (portList.hasMoreElements()) { - CommPortIdentifier id = (CommPortIdentifier) portList.nextElement(); - if (id.getPortType() == CommPortIdentifier.PORT_SERIAL) { - sb.append(id.getName() + "\n"); - } - } - logger.error("Serial port '{}' could not be found. Available ports are:\n {}", config.port, sb); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); + + updateStatus(ThingStatus.ONLINE); + + } catch (PortInUseException portInUseException) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Port in use: " + config.port); + } catch (UnsupportedCommOperationException | IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error"); + } catch (TooManyListenersException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Too many listeners to serial port."); } } } @@ -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 { private boolean interrupted = false;