diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800DeviceConnector.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800DeviceConnector.java index 8ce52f1271bf0..66ad685259722 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800DeviceConnector.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800DeviceConnector.java @@ -17,11 +17,14 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.Optional; +import java.util.Random; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.gce.internal.model.M2MMessageParser; +import org.openhab.binding.gce.internal.model.PortDefinition; import org.openhab.binding.gce.internal.model.StatusFile; import org.openhab.binding.gce.internal.model.StatusFileAccessor; import org.openhab.core.thing.ThingUID; @@ -38,16 +41,18 @@ */ @NonNullByDefault public class Ipx800DeviceConnector extends Thread { - private static final int DEFAULT_SOCKET_TIMEOUT_MS = 5000; + private static final int DEFAULT_SOCKET_TIMEOUT_MS = 10000; private static final int DEFAULT_RECONNECT_TIMEOUT_MS = 5000; private static final int MAX_KEEPALIVE_FAILURE = 3; - private static final String ENDL = "\r\n"; private final Logger logger = LoggerFactory.getLogger(Ipx800DeviceConnector.class); + private final Random randomizer = new Random(); + private final String hostname; private final int portNumber; - private final M2MMessageParser messageParser; + private final M2MMessageParser parser; private final StatusFileAccessor statusAccessor; + private final Ipx800EventListener listener; private Optional socket = Optional.empty(); private Optional input = Optional.empty(); @@ -60,19 +65,12 @@ public Ipx800DeviceConnector(String hostname, int portNumber, ThingUID uid, Ipx8 super("OH-binding-" + uid); this.hostname = hostname; this.portNumber = portNumber; - this.messageParser = new M2MMessageParser(this, listener); + this.listener = listener; + this.parser = new M2MMessageParser(listener); this.statusAccessor = new StatusFileAccessor(hostname); setDaemon(true); } - public synchronized void send(String message) { - output.ifPresentOrElse(out -> { - logger.debug("Sending '{}' to Ipx800", message); - out.write(message + ENDL); - out.flush(); - }, () -> logger.warn("Trying to send '{}' while the output stream is closed.", message)); - } - /** * Connect to the ipx800 * @@ -84,39 +82,27 @@ private void connect() throws IOException { logger.debug("Connecting to {}:{}...", hostname, portNumber); Socket socket = new Socket(hostname, portNumber); socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT_MS); - socket.getInputStream().skip(socket.getInputStream().available()); + // socket.getInputStream().skip(socket.getInputStream().available()); this.socket = Optional.of(socket); - input = Optional.of(new BufferedReader(new InputStreamReader(socket.getInputStream()))); output = Optional.of(new PrintWriter(socket.getOutputStream(), true)); + input = Optional.of(new BufferedReader(new InputStreamReader(socket.getInputStream()))); } /** * Disconnect the device */ private void disconnect() { - logger.debug("Disconnecting"); - - input.ifPresent(in -> { - try { - in.close(); - } catch (IOException ignore) { - } - input = Optional.empty(); - }); - - output.ifPresent(PrintWriter::close); - output = Optional.empty(); - socket.ifPresent(client -> { try { + logger.debug("Closing socket"); client.close(); } catch (IOException ignore) { } socket = Optional.empty(); + input = Optional.empty(); + output = Optional.empty(); }); - - logger.debug("Disconnected"); } /** @@ -127,23 +113,35 @@ public void dispose() { disconnect(); } + public synchronized void send(String message) { + output.ifPresentOrElse(out -> { + logger.debug("Sending '{}' to Ipx800", message); + out.println(message); + }, () -> logger.warn("Unable to send '{}' when the output stream is closed.", message)); + } + /** * Send an arbitrary keepalive command which cause the IPX to send an update. * If we don't receive the update maxKeepAliveFailure time, the connection is closed and reopened */ private void sendKeepalive() { - output.ifPresent(out -> { + output.ifPresentOrElse(out -> { + PortDefinition pd = PortDefinition.values()[randomizer.nextInt(PortDefinition.AS_SET.size())]; + String command = "%s%d".formatted(pd.m2mCommand, randomizer.nextInt(pd.quantity) + 1); + if (waitingKeepaliveResponse) { failedKeepalive++; - logger.debug("Sending keepalive, attempt {}", failedKeepalive); + logger.debug("Sending keepalive {}, attempt {}", command, failedKeepalive); } else { failedKeepalive = 0; - logger.debug("Sending keepalive"); + logger.debug("Sending keepalive {}", command); } - out.println("GetIn01"); - out.flush(); + + out.println(command); + parser.setExpectedResponse(command); + waitingKeepaliveResponse = true; - }); + }, () -> logger.warn("Unable to send keepAlive when the output stream is closed.")); } @Override @@ -160,7 +158,7 @@ public void run() { try { String command = in.readLine(); waitingKeepaliveResponse = false; - messageParser.unsolicitedUpdate(command); + parser.unsolicitedUpdate(command); } catch (IOException e) { handleException(e); } @@ -182,18 +180,43 @@ private void handleException(Exception e) { if (e instanceof SocketTimeoutException) { sendKeepalive(); return; + } else if (e instanceof SocketException) { + logger.debug("SocketException raised by streams while closing socket"); } else if (e instanceof IOException) { logger.warn("Communication error: '{}'. Will retry in {} ms", e, DEFAULT_RECONNECT_TIMEOUT_MS); } - messageParser.errorOccurred(e); + listener.errorOccurred(e); } } - public M2MMessageParser getParser() { - return messageParser; - } - public StatusFile readStatusFile() throws SAXException, IOException { return statusAccessor.read(); } + + /** + * Set output of the device sending the corresponding command + * + * @param targetPort + * @param targetValue + */ + public void setOutput(String targetPort, int targetValue, boolean pulse) { + logger.debug("Sending {} to {}", targetValue, targetPort); + String command = "Set%02d%s%s".formatted(Integer.parseInt(targetPort), targetValue, pulse ? "p" : ""); + send(command); + } + + /** + * Resets the counter value to 0 + * + * @param targetCounter + */ + public void resetCounter(int targetCounter) { + logger.debug("Resetting counter {} to 0", targetCounter); + send("ResetCount%d".formatted(targetCounter)); + } + + public void resetPLC() { + send("Reset"); + } + } diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800v3Handler.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800v3Handler.java index b8d04cc8ecc64..f7f6c19098509 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800v3Handler.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/handler/Ipx800v3Handler.java @@ -57,7 +57,6 @@ import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.State; -import org.openhab.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; @@ -92,8 +91,7 @@ public LongPressEvaluator(Channel channel, String port, PortData portData) { @Override public void run() { - PortData currentData = portDatas.get(port); - if (currentData != null && currentData.getValue() == 1 + if (portDatas.get(port) instanceof PortData currentData && currentData.getValue() == 1 && referenceTime.equals(currentData.getTimestamp())) { triggerChannel(eventChannelId, EVENT_LONG_PRESS); } @@ -133,29 +131,18 @@ private void readStatusFile() { connector.start(); } - if (status != null) { - for (PortDefinition portDefinition : PortDefinition.values()) { - status.getMatchingNodes(portDefinition.nodeName).forEach(node -> { - String sPortNum = node.getNodeName().replace(portDefinition.nodeName, ""); - try { - int portNum = Integer.parseInt(sPortNum) + 1; - double value = Double - .parseDouble(node.getTextContent().replace("dn", "1").replace("up", "0")); - dataReceived("%s%d".formatted(portDefinition.portName, portNum), value); - } catch (NumberFormatException e) { - logger.warn(e.getMessage()); - } - }); - } + if (status instanceof StatusFile statusFile) { + PortDefinition.AS_SET.forEach(portDefinition -> statusFile.getPorts(portDefinition).forEach( + (portNum, value) -> dataReceived("%s%d".formatted(portDefinition.portName, portNum), value))); } - } + } private void updateChannels(@Nullable StatusFile status) { List channels = new ArrayList<>(getThing().getChannels()); PortDefinition.AS_SET.forEach(portDefinition -> { - int nbElements = status != null ? status.getMaxNumberofNodeType(portDefinition) : portDefinition.quantity; + int nbElements = status != null ? status.getPorts(portDefinition).size() : portDefinition.quantity; for (int i = 0; i < nbElements; i++) { ChannelUID portChannelUID = createChannels(portDefinition, i, channels); portDatas.put(portChannelUID.getId(), new PortData()); @@ -181,7 +168,7 @@ public void dispose() { if (deviceConnector instanceof Ipx800DeviceConnector connector) { connector.dispose(); - connector = null; + deviceConnector = null; } portDatas.values().stream().forEach(PortData::dispose); @@ -205,29 +192,25 @@ private ChannelUID createChannels(PortDefinition portDefinition, int portIndex, ChannelUID mainChannelUID = new ChannelUID(groupUID, ndx); ChannelTypeUID channelType = new ChannelTypeUID(BINDING_ID, advancedChannelTypeName); switch (portDefinition) { - case ANALOG: + case ANALOG -> { addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER) .withLabel("Analog Input " + ndx).withType(channelType), channels); addIfChannelAbsent( ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-voltage"), "Number:ElectricPotential") .withType(new ChannelTypeUID(BINDING_ID, CHANNEL_VOLTAGE)).withLabel("Voltage " + ndx), channels); - break; - case CONTACT: + } + case CONTACT -> { addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.CONTACT) .withLabel("Contact " + ndx).withType(channelType), channels); addIfChannelAbsent(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-event"), null) .withType(new ChannelTypeUID(BINDING_ID, TRIGGER_CONTACT + (portIndex < 8 ? "" : "Advanced"))) .withLabel("Contact " + ndx + " Event").withKind(ChannelKind.TRIGGER), channels); - break; - case COUNTER: - addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER) - .withLabel("Counter " + ndx).withType(channelType), channels); - break; - case RELAY: - addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.SWITCH) - .withLabel("Relay " + ndx).withType(channelType), channels); - break; + } + case COUNTER -> addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER) + .withLabel("Counter " + ndx).withType(channelType), channels); + case RELAY -> addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.SWITCH) + .withLabel("Relay " + ndx).withType(channelType), channels); } addIfChannelAbsent(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-duration"), "Number:Time") @@ -244,7 +227,7 @@ public void errorOccurred(Exception e) { private boolean ignoreCondition(double newValue, PortData portData, Configuration configuration, PortDefinition portDefinition, Instant now) { - if (!portData.isInitializing()) { // Always accept if portData is not initialized + if (portData.isInitialized()) { // Always accept if portData is not initialized double prevValue = portData.getValue(); if (newValue == prevValue) { // Always reject if the value did not change return true; @@ -265,68 +248,57 @@ private boolean ignoreCondition(double newValue, PortData portData, Configuratio @Override public void dataReceived(String port, double value) { updateStatus(ThingStatus.ONLINE); - Channel channel = thing.getChannel(PortDefinition.asChannelId(port)); - if (channel != null) { + if (thing.getChannel(PortDefinition.asChannelId(port)) instanceof Channel channel) { String channelId = channel.getUID().getId(); - String groupId = channel.getUID().getGroupId(); - PortData portData = portDatas.get(channelId); - if (portData != null && groupId != null) { + + if (portDatas.get(channelId) instanceof PortData portData + && channel.getUID().getGroupId() instanceof String groupId) { Instant now = Instant.now(); - long sinceLastChange = Duration.between(portData.getTimestamp(), now).toMillis(); Configuration configuration = channel.getConfiguration(); PortDefinition portDefinition = PortDefinition.fromGroupId(groupId); if (ignoreCondition(value, portData, configuration, portDefinition, now)) { - logger.debug("Ignore condition met for port '{}' with data '{}'", port, value); + logger.trace("Ignore condition met for port '{}' with data '{}'", port, value); return; } logger.debug("About to update port '{}' with data '{}'", port, value); - State state = UnDefType.NULL; - switch (portDefinition) { - case COUNTER: - state = new DecimalType(value); - break; - case RELAY: - state = OnOffType.from(value == 1); - break; - case ANALOG: - state = new DecimalType(value); + long sinceLastChange = Duration.between(portData.getTimestamp(), now).toMillis(); + State state = switch (portDefinition) { + case COUNTER -> new DecimalType(value); + case RELAY -> OnOffType.from(value == 1); + case ANALOG -> { updateIfLinked(channelId + PROPERTY_SEPARATOR + CHANNEL_VOLTAGE, new QuantityType<>(value * ANALOG_SAMPLING, Units.VOLT)); - break; - case CONTACT: - DigitalInputConfiguration config = configuration.as(DigitalInputConfiguration.class); + yield new DecimalType(value); + } + case CONTACT -> { portData.cancelPulsing(); - state = value == 1 ? OpenClosedType.CLOSED : OpenClosedType.OPEN; - switch ((OpenClosedType) state) { - case CLOSED: - if (config.longPressTime != 0 && !portData.isInitializing()) { - scheduler.schedule(new LongPressEvaluator(channel, port, portData), - config.longPressTime, TimeUnit.MILLISECONDS); - } else if (config.pulsePeriod != 0) { - portData.setPulsing(scheduler.scheduleWithFixedDelay(() -> { - triggerPushButtonChannel(channel, EVENT_PULSE); - }, config.pulsePeriod, config.pulsePeriod, TimeUnit.MILLISECONDS)); - if (config.pulseTimeout != 0) { - scheduler.schedule(portData::cancelPulsing, config.pulseTimeout, - TimeUnit.MILLISECONDS); - } - } - break; - case OPEN: - if (!portData.isInitializing() && config.longPressTime != 0 - && sinceLastChange < config.longPressTime) { - triggerPushButtonChannel(channel, EVENT_SHORT_PRESS); + DigitalInputConfiguration config = configuration.as(DigitalInputConfiguration.class); + + if (value == 1) { // CLOSED + if (config.longPressTime != 0 && portData.isInitialized()) { + scheduler.schedule(new LongPressEvaluator(channel, port, portData), + config.longPressTime, TimeUnit.MILLISECONDS); + } else if (config.pulsePeriod != 0) { + portData.setPulsing(scheduler.scheduleWithFixedDelay(() -> { + triggerPushButtonChannel(channel, EVENT_PULSE); + }, config.pulsePeriod, config.pulsePeriod, TimeUnit.MILLISECONDS)); + if (config.pulseTimeout != 0) { + portData.setPulseCanceler(scheduler.schedule(portData::cancelPulsing, + config.pulseTimeout, TimeUnit.MILLISECONDS)); } - break; + } + } else if (portData.isInitialized() && sinceLastChange < config.longPressTime) { + triggerPushButtonChannel(channel, EVENT_SHORT_PRESS); } - if (!portData.isInitializing()) { + if (portData.isInitialized()) { triggerPushButtonChannel(channel, value == 1 ? EVENT_PRESSED : EVENT_RELEASED); } - break; - } + yield value == 1 ? OpenClosedType.CLOSED : OpenClosedType.OPEN; + } + }; updateIfLinked(channelId, state); - if (!portData.isInitializing()) { + if (portData.isInitialized()) { updateIfLinked(channelId + PROPERTY_SEPARATOR + CHANNEL_LAST_STATE_DURATION, new QuantityType<>(sinceLastChange / 1000, Units.SECOND)); } @@ -354,21 +326,18 @@ protected void triggerPushButtonChannel(Channel channel, String event) { public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Received channel: {}, command: {}", channelUID, command); - Channel channel = thing.getChannel(channelUID.getId()); - String groupId = channelUID.getGroupId(); - - if (channel == null || groupId == null) { - return; - } - if (command instanceof OnOffType onOffCommand && isValidPortId(channelUID) - && PortDefinition.fromGroupId(groupId) == PortDefinition.RELAY + if (thing.getChannel(channelUID.getId()) instanceof Channel channel + && channelUID.getGroupId() instanceof String groupId // + && command instanceof OnOffType onOffCommand // + && isValidPortId(channelUID) // + && PortDefinition.RELAY.equals(PortDefinition.fromGroupId(groupId)) && deviceConnector instanceof Ipx800DeviceConnector connector) { RelayOutputConfiguration config = channel.getConfiguration().as(RelayOutputConfiguration.class); - String id = channelUID.getIdWithoutGroup(); - connector.getParser().setOutput(id, onOffCommand == OnOffType.ON ? 1 : 0, config.pulse); - return; + connector.setOutput(channelUID.getIdWithoutGroup(), OnOffType.ON.equals(onOffCommand) ? 1 : 0, + config.pulse); + } else { + logger.debug("Can not handle command '{}' on channel '{}'", command, channelUID); } - logger.debug("Can not handle command '{}' on channel '{}'", command, channelUID); } private boolean isValidPortId(ChannelUID channelUID) { @@ -377,13 +346,13 @@ private boolean isValidPortId(ChannelUID channelUID) { public void resetCounter(int counter) { if (deviceConnector instanceof Ipx800DeviceConnector connector) { - connector.getParser().resetCounter(counter); + connector.resetCounter(counter); } } public void reset() { if (deviceConnector instanceof Ipx800DeviceConnector connector) { - connector.getParser().resetPLC(); + connector.resetPLC(); } } diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/M2MMessageParser.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/M2MMessageParser.java index b9d4c526e86d4..6ba4d88990c6e 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/M2MMessageParser.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/M2MMessageParser.java @@ -15,7 +15,6 @@ import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.gce.internal.handler.Ipx800DeviceConnector; import org.openhab.binding.gce.internal.handler.Ipx800EventListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,22 +32,19 @@ public class M2MMessageParser { .compile("I=" + IO_DESCRIPTOR + "&O=" + IO_DESCRIPTOR + "&([AC]\\d{1,2}=\\d+&)*[^I]*"); private final Logger logger = LoggerFactory.getLogger(M2MMessageParser.class); - private final Ipx800DeviceConnector connector; private final Ipx800EventListener listener; private String expectedResponse = ""; - public M2MMessageParser(Ipx800DeviceConnector connector, Ipx800EventListener listener) { - this.connector = connector; + public M2MMessageParser(Ipx800EventListener listener) { this.listener = listener; } - /** - * - * @param data - */ public void unsolicitedUpdate(String data) { - if (IO_PATTERN.matcher(data).matches()) { + if ("OK".equals(data)) { // If OK, do nothing special + } else if ("? Bad command".equals(data)) { + logger.warn(data); + } else if (IO_PATTERN.matcher(data).matches()) { PortDefinition portDefinition = PortDefinition.fromM2MCommand(expectedResponse); decodeDataLine(portDefinition, data); } else if (VALIDATION_PATTERN.matcher(data).matches()) { @@ -72,8 +68,9 @@ public void unsolicitedUpdate(String data) { } } else if (!expectedResponse.isEmpty()) { setStatus(expectedResponse, Double.parseDouble(data)); + } else { + logger.warn("Unable to handle data received: {}", data); } - expectedResponse = ""; } @@ -84,47 +81,17 @@ private void decodeDataLine(PortDefinition portDefinition, String data) { } private void setStatus(String port, double value) { - logger.debug("Received {} : {}", port, value); + logger.debug("Received {} on port {}", value, port); listener.dataReceived(port, value); } public void setExpectedResponse(String expectedResponse) { if (expectedResponse.endsWith("s")) { // GetInputs or GetOutputs this.expectedResponse = expectedResponse; - } else { // GetAnx or GetCountx - PortDefinition portType = PortDefinition.fromM2MCommand(expectedResponse); - this.expectedResponse = expectedResponse.replaceAll(portType.m2mCommand, portType.portName); + return; } - } - - /** - * Set output of the device sending the corresponding command - * - * @param targetPort - * @param targetValue - */ - public void setOutput(String targetPort, int targetValue, boolean pulse) { - logger.debug("Sending {} to {}", targetValue, targetPort); - String command = "Set%02d%s%s".formatted(Integer.parseInt(targetPort), targetValue, pulse ? "p" : ""); - connector.send(command); - } - - /** - * Resets the counter value to 0 - * - * @param targetCounter - */ - public void resetCounter(int targetCounter) { - logger.debug("Resetting counter {} to 0", targetCounter); - connector.send("ResetCount%d".formatted(targetCounter)); - } - - public void errorOccurred(Exception e) { - logger.warn("Error received from connector : {}", e.getMessage()); - listener.errorOccurred(e); - } - - public void resetPLC() { - connector.send("Reset"); + // GetAnx or GetCountx + PortDefinition portType = PortDefinition.fromM2MCommand(expectedResponse); + this.expectedResponse = expectedResponse.replaceAll(portType.m2mCommand, portType.portName); } } diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortData.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortData.java index c4bbe9f5dd993..390346ad02e91 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortData.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortData.java @@ -13,10 +13,10 @@ package org.openhab.binding.gce.internal.model; import java.time.Instant; -import java.util.Optional; import java.util.concurrent.ScheduledFuture; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; /** * The {@link PortData} is responsible for holding data regarding current status of a port. @@ -27,15 +27,27 @@ public class PortData { private double value = -1; private Instant timestamp = Instant.now(); - private Optional> pulsing = Optional.empty(); + private @Nullable ScheduledFuture pulsing; + private @Nullable ScheduledFuture pulseCanceler; public void cancelPulsing() { - pulsing.ifPresent(pulse -> pulse.cancel(true)); - pulsing = Optional.empty(); + if (pulsing instanceof ScheduledFuture job) { + job.cancel(true); + pulsing = null; + } + cancelCanceler(); + } + + public void cancelCanceler() { + if (pulseCanceler instanceof ScheduledFuture job) { + job.cancel(true); + pulseCanceler = null; + } } public void dispose() { cancelPulsing(); + cancelCanceler(); } public void setData(double value, Instant timestamp) { @@ -53,10 +65,14 @@ public Instant getTimestamp() { public void setPulsing(ScheduledFuture pulsing) { cancelPulsing(); - this.pulsing = Optional.of(pulsing); + this.pulsing = pulsing; + } + + public boolean isInitialized() { + return value != -1; } - public boolean isInitializing() { - return value == -1; + public void setPulseCanceler(ScheduledFuture pulseCanceler) { + this.pulseCanceler = pulseCanceler; } } diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortDefinition.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortDefinition.java index dae3265e7be03..86bc9c9dc4076 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortDefinition.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/PortDefinition.java @@ -34,7 +34,7 @@ public enum PortDefinition { public final String m2mCommand; // associated M2M command public final int quantity; // base number of ports - PortDefinition(String nodeName, String portName, String m2mCommand, int quantity) { + private PortDefinition(String nodeName, String portName, String m2mCommand, int quantity) { this.nodeName = nodeName; this.portName = portName; this.m2mCommand = m2mCommand; diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFile.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFile.java index dca907e6861a8..6b7f285d000d8 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFile.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFile.java @@ -12,14 +12,15 @@ */ package org.openhab.binding.gce.internal.model; -import java.util.Comparator; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.stream.IntStream; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** @@ -29,12 +30,14 @@ */ @NonNullByDefault public class StatusFile { - + private final Logger logger = LoggerFactory.getLogger(StatusFile.class); private final Element root; + private final NodeList childs; public StatusFile(Document doc) { this.root = doc.getDocumentElement(); root.normalize(); + this.childs = root.getChildNodes(); } public String getMac() { @@ -45,14 +48,20 @@ public String getVersion() { return root.getElementsByTagName("version").item(0).getTextContent(); } - public List getMatchingNodes(String criteria) { - NodeList nodeList = root.getChildNodes(); - return IntStream.range(0, nodeList.getLength()).boxed().map(nodeList::item) - .filter(node -> node.getNodeName().startsWith(criteria)).sorted(Comparator.comparing(Node::getNodeName)) - .toList(); - } + public Map getPorts(PortDefinition portDefinition) { + Map result = new HashMap<>(); + + String searched = portDefinition.nodeName; - public int getMaxNumberofNodeType(PortDefinition portDefinition) { - return getMatchingNodes(portDefinition.nodeName).size(); + IntStream.range(0, childs.getLength()).boxed().map(childs::item) + .filter(node -> node.getNodeName().startsWith(searched)).forEach(node -> { + try { + result.put(Integer.parseInt(node.getNodeName().replace(searched, "")) + 1, + Double.parseDouble(node.getTextContent().replace("dn", "1").replace("up", "0"))); + } catch (NumberFormatException e) { + logger.warn(e.getMessage()); + } + }); + return result; } } diff --git a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFileAccessor.java b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFileAccessor.java index 16a6e15cb9a1f..9c17f49471cb4 100644 --- a/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFileAccessor.java +++ b/bundles/org.openhab.binding.gce/src/main/java/org/openhab/binding/gce/internal/model/StatusFileAccessor.java @@ -22,7 +22,7 @@ import org.xml.sax.SAXException; /** - * This class takes care of interpreting the status.xml file + * This class takes care of providing the IPX status file * * @author Gaƫl L'hopital - Initial contribution */ @@ -34,7 +34,7 @@ public class StatusFileAccessor { private final String url; public StatusFileAccessor(String hostname) { - this.url = String.format(URL_TEMPLATE, hostname); + this.url = URL_TEMPLATE.formatted(hostname); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setXIncludeAware(false); factory.setExpandEntityReferences(false); @@ -45,12 +45,11 @@ public StatusFileAccessor(String hostname) { factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { - throw new IllegalArgumentException("Error initializing StatusFileInterpreter", e); + throw new IllegalArgumentException("Error initializing StatusFileAccessor", e); } } public StatusFile read() throws SAXException, IOException { - StatusFile document = new StatusFile(builder.parse(url)); - return document; + return new StatusFile(builder.parse(url)); } }