]> git.basschouten.com Git - openhab-addons.git/blob
fdb656d5b7719042cb0821db3fab709257c06e4b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.smartmeter.internal.sml;
14
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;
23
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;
37
38 /**
39  * Represents a serial SML device connector.
40  *
41  * @author Matthias Steigenberger - Initial contribution
42  * @author Mathias Gilhuber - Also-By
43  */
44 @NonNullByDefault
45 public final class SmlSerialConnector extends ConnectorBase<SmlFile> {
46
47     private static final Transport TRANSPORT = new Transport();
48
49     private Supplier<SerialPortManager> serialManagerSupplier;
50     @NonNullByDefault({})
51     private SerialPort serialPort;
52     @Nullable
53     private DataInputStream is;
54     @Nullable
55     private DataOutputStream os;
56     private int baudrate;
57
58     /**
59      * Constructor to create a serial connector instance.
60      *
61      * @param portName the port where the device is connected as defined in openHAB configuration.
62      */
63     public SmlSerialConnector(Supplier<SerialPortManager> serialPortManagerSupplier, String portName) {
64         super(portName);
65         this.serialManagerSupplier = serialPortManagerSupplier;
66     }
67
68     /**
69      * Constructor to create a serial connector instance with a specific serial parameter.
70      *
71      * @param portName the port where the device is connected as defined in openHAB configuration.
72      * @param baudrate
73      * @throws IOException
74      */
75     public SmlSerialConnector(Supplier<SerialPortManager> serialPortManagerSupplier, String portName, int baudrate,
76             int baudrateChangeDelay) {
77         this(serialPortManagerSupplier, portName);
78         this.baudrate = baudrate;
79     }
80
81     @Override
82     protected SmlFile readNext(byte @Nullable [] initMessage) throws IOException {
83         if (initMessage != null) {
84             logger.debug("Writing init message: {}", HexUtils.bytesToHex(initMessage, " "));
85             if (os != null) {
86                 os.write(initMessage);
87                 os.flush();
88             }
89         }
90
91         // read out the whole buffer. We are only interested in the most recent SML file.
92         Stack<SmlFile> smlFiles = new Stack<>();
93         do {
94             logger.trace("Reading {}. SML message", smlFiles.size() + 1);
95             smlFiles.push(TRANSPORT.getSMLFile(is));
96         } while (is != null && is.available() > 0);
97         if (smlFiles.isEmpty()) {
98             throw new IOException(getPortName() + " : There is no SML file in buffer. Try to increase Refresh rate.");
99         }
100         logger.debug("{} : Read {} SML files from Buffer", this.getPortName(), smlFiles.size());
101         return smlFiles.pop();
102     }
103
104     @Override
105     public void openConnection() throws IOException {
106         closeConnection();
107         SerialPortIdentifier id = serialManagerSupplier.get().getIdentifier(getPortName());
108         if (id != null) {
109             try {
110                 serialPort = id.open("meterreaderbinding", 0);
111             } catch (PortInUseException e) {
112                 throw new IOException(MessageFormat
113                         .format("Error at SerialConnector.openConnection: unable to open port {0}.", getPortName()), e);
114             }
115             SerialParameter serialParameter = SerialParameter._8N1;
116             int baudrateToUse = this.baudrate == Baudrate.AUTO.getBaudrate() ? Baudrate._9600.getBaudrate()
117                     : this.baudrate;
118             try {
119                 serialPort.setSerialPortParams(baudrateToUse, serialParameter.getDatabits(),
120                         serialParameter.getStopbits(), serialParameter.getParity());
121                 serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT);
122                 try {
123                     serialPort.enableReceiveTimeout(100);
124                 } catch (UnsupportedCommOperationException e) {
125                     // doesn't matter (rfc2217 is not supporting this)
126                 }
127             } catch (UnsupportedCommOperationException e) {
128                 throw new IOException(MessageFormat.format(
129                         "Error at SerialConnector.openConnection: unable to set serial port parameters for port {0}.",
130                         getPortName()), e);
131             }
132             // serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT);
133             serialPort.notifyOnDataAvailable(true);
134             is = new DataInputStream(new BufferedInputStream(serialPort.getInputStream()));
135             os = new DataOutputStream(new BufferedOutputStream(serialPort.getOutputStream()));
136         } else {
137             throw new IllegalStateException(MessageFormat.format("No provider for port {0} found", getPortName()));
138         }
139     }
140
141     /**
142      * @{inheritDoc}
143      */
144     @Override
145     public void closeConnection() {
146         try {
147             if (is != null) {
148                 is.close();
149                 is = null;
150             }
151         } catch (IOException e) {
152             logger.error("Failed to close serial input stream", e);
153         }
154         try {
155             if (os != null) {
156                 os.close();
157                 os = null;
158             }
159         } catch (IOException e) {
160             logger.error("Failed to close serial output stream", e);
161         }
162         if (serialPort != null) {
163             serialPort.close();
164             serialPort = null;
165         }
166     }
167
168     @Override
169     protected boolean applyPeriod() {
170         return true;
171     }
172 }