]> git.basschouten.com Git - openhab-addons.git/blob
5071d5ac4d4a9af145a22ec4191f0b6ad34f0d23
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.teleinfo.internal.serial;
14
15 import static org.openhab.binding.teleinfo.internal.TeleinfoBindingConstants.*;
16
17 import java.io.IOException;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.teleinfo.internal.data.Frame;
24 import org.openhab.binding.teleinfo.internal.handler.TeleinfoAbstractControllerHandler;
25 import org.openhab.binding.teleinfo.internal.reader.io.serialport.InvalidFrameException;
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.openhab.core.library.types.DecimalType;
32 import org.openhab.core.thing.Bridge;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.thing.ThingStatusDetail;
36 import org.openhab.core.types.Command;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link TeleinfoSerialControllerHandler} class defines a handler for serial controller.
42  *
43  * @author Nicolas SIBERIL - Initial contribution
44  */
45 @NonNullByDefault
46 public class TeleinfoSerialControllerHandler extends TeleinfoAbstractControllerHandler
47         implements TeleinfoReceiveThreadListener {
48
49     private final Logger logger = LoggerFactory.getLogger(TeleinfoSerialControllerHandler.class);
50
51     private static final int SERIAL_RECEIVE_TIMEOUT_MS = 250;
52     private static final int RECEIVER_THREAD_JOIN_DELAY_MS = 500;
53
54     private SerialPortManager serialPortManager;
55     private @Nullable SerialPort serialPort;
56     private @Nullable TeleinfoReceiveThread receiveThread;
57     private @Nullable ScheduledFuture<?> keepAliveThread;
58     private long invalidFrameCounter = 0;
59
60     public TeleinfoSerialControllerHandler(Bridge thing, SerialPortManager serialPortManager) {
61         super(thing);
62         this.serialPortManager = serialPortManager;
63     }
64
65     @Override
66     public void initialize() {
67         invalidFrameCounter = 0;
68
69         keepAliveThread = scheduler.scheduleWithFixedDelay(() -> {
70             if (!isInitialized()) {
71                 openSerialPortAndStartReceiving();
72                 updateStatus(ThingStatus.UNKNOWN);
73             }
74             logger.debug("Check Teleinfo receiveThread status...");
75             logger.debug("isInitialized() = {}", isInitialized());
76             TeleinfoReceiveThread receiveThreadRef = receiveThread;
77             if (receiveThreadRef != null) {
78                 logger.debug("receiveThread.isAlive() = {}", receiveThreadRef.isAlive());
79             }
80             if (isInitialized() && (receiveThreadRef == null || !receiveThreadRef.isAlive())) {
81                 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, ERROR_UNKNOWN_RETRY_IN_PROGRESS);
82                 logger.info("Try to restart Teleinfo receiving...");
83                 stopReceivingAndCloseSerialPort();
84                 openSerialPortAndStartReceiving();
85             }
86         }, 0, 60, TimeUnit.SECONDS);
87     }
88
89     @Override
90     public void dispose() {
91         ScheduledFuture<?> keepAliveThreadRef = keepAliveThread;
92         if (keepAliveThreadRef != null) {
93             keepAliveThreadRef.cancel(true);
94             keepAliveThread = null;
95         }
96         stopReceivingAndCloseSerialPort();
97
98         super.dispose();
99     }
100
101     @Override
102     public void handleCommand(ChannelUID channelUID, Command command) {
103     }
104
105     @Override
106     public void onFrameReceived(Frame frame) {
107         updateStatus(ThingStatus.ONLINE);
108         fireOnFrameReceivedEvent(frame);
109     }
110
111     @Override
112     public void onInvalidFrameReceived(TeleinfoReceiveThread receiveThread, InvalidFrameException error) {
113         invalidFrameCounter++;
114         updateState(THING_SERIAL_CONTROLLER_CHANNEL_INVALID_FRAME_COUNTER, new DecimalType(invalidFrameCounter));
115     }
116
117     @Override
118     public void onSerialPortInputStreamIOException(TeleinfoReceiveThread receiveThread, IOException e) {
119         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, ERROR_UNKNOWN_RETRY_IN_PROGRESS);
120     }
121
122     @Override
123     public void continueOnReadNextFrameTimeoutException() {
124         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, ERROR_UNKNOWN_RETRY_IN_PROGRESS);
125     }
126
127     private void openSerialPortAndStartReceiving() {
128         TeleinfoSerialControllerConfiguration config = getConfigAs(TeleinfoSerialControllerConfiguration.class);
129
130         if (config.serialport.trim().isEmpty()) {
131             logger.warn("Teleinfo port is not set.");
132             return;
133         }
134
135         logger.debug("Connecting to serial port '{}'...", config.serialport);
136         String currentOwner = null;
137         try {
138             final SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(config.serialport);
139             logger.debug("portIdentifier = {}", portIdentifier);
140             if (portIdentifier == null) {
141                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
142                         ERROR_OFFLINE_SERIAL_NOT_FOUND);
143                 return;
144             }
145             logger.debug("Opening portIdentifier");
146             currentOwner = portIdentifier.getCurrentOwner();
147             logger.debug("portIdentifier.getCurrentOwner() = {}", currentOwner);
148             SerialPort commPort = portIdentifier.open("org.openhab.binding.teleinfo", 5000);
149             serialPort = commPort;
150
151             TeleinfoTicMode ticMode = TeleinfoTicMode.valueOf(config.ticMode);
152             commPort.setSerialPortParams(ticMode.getBitrate(), SerialPort.DATABITS_7, SerialPort.STOPBITS_1,
153                     SerialPort.PARITY_EVEN);
154             try {
155                 commPort.enableReceiveThreshold(1);
156             } catch (UnsupportedCommOperationException e) {
157                 // rfc2217
158             }
159             try {
160                 commPort.enableReceiveTimeout(SERIAL_RECEIVE_TIMEOUT_MS);
161             } catch (UnsupportedCommOperationException e) {
162                 // rfc2217
163             }
164             logger.debug("Starting receive thread");
165             TeleinfoReceiveThread receiveThread = new TeleinfoReceiveThread(commPort, this,
166                     config.autoRepairInvalidADPSgroupLine, ticMode, config.verifyChecksum);
167             this.receiveThread = receiveThread;
168             receiveThread.start();
169
170             logger.debug("Connected to serial port '{}'", config.serialport);
171         } catch (PortInUseException e) {
172             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
173                     ERROR_OFFLINE_SERIAL_INUSE);
174         } catch (UnsupportedCommOperationException e) {
175             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
176                     ERROR_OFFLINE_SERIAL_UNSUPPORTED);
177         }
178     }
179
180     private void stopReceivingAndCloseSerialPort() {
181         TeleinfoReceiveThread receiveThreadRef = receiveThread;
182         if (receiveThreadRef != null) {
183             receiveThreadRef.interrupt();
184             try {
185                 receiveThreadRef.join(RECEIVER_THREAD_JOIN_DELAY_MS);
186             } catch (InterruptedException e) {
187             }
188             receiveThreadRef.setListener(null);
189             receiveThread = null;
190         }
191         SerialPort serialPortRef = serialPort;
192         if (serialPortRef != null) {
193             serialPortRef.close();
194             serialPort = null;
195         }
196     }
197 }