]> git.basschouten.com Git - openhab-addons.git/blob
ef455e24b6fa787d93274c8f46b5e83fa69605da
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.knx.internal.client;
14
15 import java.io.ByteArrayInputStream;
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.OutputStream;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.stream.Collectors;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.core.io.transport.serial.PortInUseException;
27 import org.openhab.core.io.transport.serial.SerialPort;
28 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
29 import org.openhab.core.io.transport.serial.SerialPortManager;
30 import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import aQute.bnd.annotation.spi.ServiceProvider;
35 import tuwien.auto.calimero.KNXException;
36 import tuwien.auto.calimero.serial.spi.SerialCom;
37
38 /**
39  * The {@link SerialTransportAdapter} provides org.openhab.core.io.transport.serial
40  * services to the Calimero library.
41  * 
42  * {@literal @}ServiceProvider annotation (biz.aQute.bnd.annotation) automatically creates the file
43  * /META-INF/services/tuwien.auto.calimero.serial.spi.SerialCom
44  * to register SerialTransportAdapter to the service loader.
45  * Additional attributes for SerialTansportAdapter can be specified as well, e.g.
46  * attribute = { "position=1" }
47  * and will be part of MANIFEST.MF
48  * 
49  * @author Holger Friedrich - Initial contribution
50  */
51 @ServiceProvider(value = SerialCom.class)
52 @NonNullByDefault
53 public class SerialTransportAdapter implements SerialCom {
54     private static final int OPEN_TIMEOUT_MS = 200;
55     private static final int RECEIVE_TIMEOUT_MS = 5;
56     private static final int RECEIVE_THRESHOLD = 1024;
57     private static final int BAUDRATE = 19200;
58
59     private Logger logger = LoggerFactory.getLogger(SerialTransportAdapter.class);
60     @Nullable
61     private static SerialPortManager serialPortManager = null;
62     @Nullable
63     private SerialPort serialPort = null;
64
65     public static void setSerialPortManager(SerialPortManager serialPortManager) {
66         SerialTransportAdapter.serialPortManager = serialPortManager;
67     }
68
69     public SerialTransportAdapter() {
70     }
71
72     @Override
73     public void open(@Nullable String portId) throws IOException, KNXException {
74         if (portId == null) {
75             throw new IOException("Port not available");
76         }
77         logger = LoggerFactory.getLogger("SerialTransportAdapter:" + portId);
78
79         final @Nullable SerialPortManager tmpSerialPortManager = serialPortManager;
80         if (tmpSerialPortManager == null) {
81             throw new IOException("PortManager not available");
82         }
83         try {
84             SerialPortIdentifier portIdentifier = tmpSerialPortManager.getIdentifier(portId);
85             if (portIdentifier != null) {
86                 logger.trace("Trying to open port {}", portId);
87                 SerialPort serialPort = portIdentifier.open(this.getClass().getName(), OPEN_TIMEOUT_MS);
88                 // apply default settings for com port, may be overwritten by caller
89                 serialPort.setSerialPortParams(BAUDRATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
90                         SerialPort.PARITY_EVEN);
91                 serialPort.enableReceiveThreshold(RECEIVE_THRESHOLD);
92                 serialPort.enableReceiveTimeout(RECEIVE_TIMEOUT_MS);
93                 this.serialPort = serialPort;
94
95                 // Notification / event listeners are available and may be used to log/trace com failures
96                 // serialPort.notifyOnDataAvailable(true);
97                 logger.trace("Port opened successfully");
98             } else {
99                 logger.info("Port {} not available", portId);
100                 throw new IOException("Port " + portId + " not available");
101             }
102         } catch (PortInUseException e) {
103             logger.info("Port {} already in use", portId);
104             throw new IOException("Port " + portId + " already in use", e);
105         } catch (UnsupportedCommOperationException e) {
106             logger.info("Port {} unsupported com operation", portId);
107             throw new IOException("Port " + portId + " unsupported com operation", e);
108         }
109     }
110
111     // SerialCom extends AutoCloseable, close() throws Exception
112     @Override
113     public void close() {
114         logger.trace("Closing serial port");
115         final @Nullable SerialPort tmpSerialPort = serialPort;
116         if (tmpSerialPort != null) {
117             tmpSerialPort.close();
118             serialPort = null;
119         }
120     }
121
122     @Override
123     public @Nullable InputStream inputStream() {
124         final @Nullable SerialPort tmpSerialPort = serialPort;
125         if (tmpSerialPort != null) {
126             try {
127                 return tmpSerialPort.getInputStream();
128             } catch (IOException e) {
129                 logger.info("Cannot open input stream");
130             }
131         }
132         // should not throw, create a dummy return value
133         byte[] buf = {};
134         return new ByteArrayInputStream(buf);
135     }
136
137     @Override
138     public @Nullable OutputStream outputStream() {
139         final @Nullable SerialPort tmpSerialPort = serialPort;
140         if (tmpSerialPort != null) {
141             try {
142                 return tmpSerialPort.getOutputStream();
143             } catch (IOException e) {
144                 logger.info("Cannot open output stream");
145             }
146         }
147         // should not throw, create a dummy return value
148         return new ByteArrayOutputStream(0);
149     }
150
151     // disable NonNullByDefault for this function, legacy interface List<String>
152     @NonNullByDefault({})
153     @Override
154     public List<String> portIdentifiers() {
155         final @Nullable SerialPortManager tmpSerialPortManager = serialPortManager;
156         if (tmpSerialPortManager == null) {
157             return Collections.emptyList();
158         }
159         // typecast only required to avoid warning about less-annotated type
160         return (List<String>) tmpSerialPortManager.getIdentifiers().map(SerialPortIdentifier::getName)
161                 .collect(Collectors.toList());
162     }
163
164     @Override
165     public int baudRate() throws IOException {
166         final @Nullable SerialPort tmpSerialPort = serialPort;
167         if (tmpSerialPort == null) {
168             throw new IOException("Port not available");
169         }
170         return tmpSerialPort.getBaudRate();
171     }
172
173     @Override
174     public void setSerialPortParams(final int baudrate, final int databits, @Nullable StopBits stopbits,
175             @Nullable Parity parity) throws IOException {
176         final @Nullable SerialPort tmpSerialPort = serialPort;
177         if (tmpSerialPort == null) {
178             throw new IOException("Port not available");
179         }
180         if ((stopbits == null) || (parity == null)) {
181             throw new IOException("Invalid parameters");
182         }
183         try {
184             tmpSerialPort.setSerialPortParams(baudrate, databits, stopbits.value(), parity.value());
185         } catch (final UnsupportedCommOperationException e) {
186             throw new IOException("Setting serial port parameters for " + tmpSerialPort.getName() + " failed", e);
187         }
188     }
189
190     @Override
191     public void setFlowControlMode(@Nullable FlowControl mode) throws IOException {
192         final @Nullable SerialPort tmpSerialPort = serialPort;
193         if (tmpSerialPort == null) {
194             throw new IOException("Port not available");
195         }
196         if (mode == null) {
197             throw new IOException("Invalid parameters");
198         }
199         if (mode == FlowControl.None) {
200             try {
201                 tmpSerialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
202             } catch (final UnsupportedCommOperationException e) {
203                 throw new IOException("Setting flow control parameters for " + tmpSerialPort.getName() + " failed", e);
204             }
205         } else {
206             logger.warn("Unknown FlowControl mode {}", mode);
207             throw new IOException("Invalid flow mode");
208         }
209     }
210 }