2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.smartmeter.internal.sml;
15 import java.io.BufferedInputStream;
16 import java.io.BufferedOutputStream;
17 import java.io.DataInputStream;
18 import java.io.DataOutputStream;
19 import java.io.IOException;
20 import java.text.MessageFormat;
21 import java.util.Stack;
22 import java.util.function.Supplier;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.smartmeter.connectors.ConnectorBase;
27 import org.openhab.binding.smartmeter.internal.helper.Baudrate;
28 import org.openhab.binding.smartmeter.internal.helper.SerialParameter;
29 import org.openhab.core.io.transport.serial.PortInUseException;
30 import org.openhab.core.io.transport.serial.SerialPort;
31 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
32 import org.openhab.core.io.transport.serial.SerialPortManager;
33 import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
34 import org.openhab.core.util.HexUtils;
35 import org.openmuc.jsml.structures.SmlFile;
36 import org.openmuc.jsml.transport.Transport;
39 * Represents a serial SML device connector.
41 * @author Matthias Steigenberger - Initial contribution
42 * @author Mathias Gilhuber - Also-By
45 public final class SmlSerialConnector extends ConnectorBase<SmlFile> {
47 private static final Transport TRANSPORT = new Transport();
49 private Supplier<SerialPortManager> serialManagerSupplier;
51 private SerialPort serialPort;
53 private DataInputStream is;
55 private DataOutputStream os;
59 * Constructor to create a serial connector instance.
61 * @param portName the port where the device is connected as defined in openHAB configuration.
63 public SmlSerialConnector(Supplier<SerialPortManager> serialPortManagerSupplier, String portName) {
65 this.serialManagerSupplier = serialPortManagerSupplier;
69 * Constructor to create a serial connector instance with a specific serial parameter.
71 * @param portName the port where the device is connected as defined in openHAB configuration.
74 public SmlSerialConnector(Supplier<SerialPortManager> serialPortManagerSupplier, String portName, int baudrate,
75 int baudrateChangeDelay) {
76 this(serialPortManagerSupplier, portName);
77 this.baudrate = baudrate;
81 protected SmlFile readNext(byte @Nullable [] initMessage) throws IOException {
82 if (initMessage != null) {
83 logger.debug("Writing init message: {}", HexUtils.bytesToHex(initMessage, " "));
85 os.write(initMessage);
90 // read out the whole buffer. We are only interested in the most recent SML file.
91 Stack<SmlFile> smlFiles = new Stack<>();
93 logger.trace("Reading {}. SML message", smlFiles.size() + 1);
94 smlFiles.push(TRANSPORT.getSMLFile(is));
95 } while (is != null && is.available() > 0);
96 if (smlFiles.isEmpty()) {
97 throw new IOException(getPortName() + " : There is no SML file in buffer. Try to increase Refresh rate.");
99 logger.debug("{} : Read {} SML files from Buffer", this.getPortName(), smlFiles.size());
100 return smlFiles.pop();
104 public void openConnection() throws IOException {
106 SerialPortIdentifier id = serialManagerSupplier.get().getIdentifier(getPortName());
109 serialPort = id.open("meterreaderbinding", 0);
110 } catch (PortInUseException e) {
111 throw new IOException(MessageFormat
112 .format("Error at SerialConnector.openConnection: unable to open port {0}.", getPortName()), e);
114 SerialParameter serialParameter = SerialParameter._8N1;
115 int baudrateToUse = this.baudrate == Baudrate.AUTO.getBaudrate() ? Baudrate._9600.getBaudrate()
118 serialPort.setSerialPortParams(baudrateToUse, serialParameter.getDatabits(),
119 serialParameter.getStopbits(), serialParameter.getParity());
120 serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT);
122 serialPort.enableReceiveTimeout(100);
123 } catch (UnsupportedCommOperationException e) {
124 // doesn't matter (rfc2217 is not supporting this)
126 } catch (UnsupportedCommOperationException e) {
127 throw new IOException(MessageFormat.format(
128 "Error at SerialConnector.openConnection: unable to set serial port parameters for port {0}.",
131 // serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT);
132 serialPort.notifyOnDataAvailable(true);
133 is = new DataInputStream(new BufferedInputStream(serialPort.getInputStream()));
134 os = new DataOutputStream(new BufferedOutputStream(serialPort.getOutputStream()));
136 throw new IllegalStateException(MessageFormat.format("No provider for port {0} found", getPortName()));
144 public void closeConnection() {
150 } catch (IOException e) {
151 logger.error("Failed to close serial input stream", e);
158 } catch (IOException e) {
159 logger.error("Failed to close serial output stream", e);
161 if (serialPort != null) {
168 protected boolean applyPeriod() {