]> git.basschouten.com Git - openhab-addons.git/blob
438b3062c400140b4a5a06a68d38b32d68073c5b
[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.dsmr.internal.device;
14
15 import java.util.Optional;
16 import java.util.concurrent.Semaphore;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.openhab.binding.dsmr.internal.device.connector.DSMRErrorStatus;
20 import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * The {@link DSMRDeviceRunnable} runs a {@link DSMRDevice} and blocks until it is restarted or shutdown. If it is
26  * restarted it will restart the {@link DSMRDevice}. If it is shutdown the run will end. By using a semaphore to restart
27  * and shutdown this class handles the actual {@link DSMRDevice}, while threads calling restart and shutdown can finish
28  * fast.
29  *
30  * @author Hilbrand Bouwkamp - Initial contribution
31  */
32 @NonNullByDefault
33 public class DSMRDeviceRunnable implements Runnable {
34     private final Logger logger = LoggerFactory.getLogger(DSMRDeviceRunnable.class);
35     private final Semaphore semaphore = new Semaphore(0);
36     private final DSMRDevice device;
37     private final P1TelegramListener portEventListener;
38
39     /**
40      * Keeps state of running. If false run will stop.
41      */
42     private boolean running;
43
44     /**
45      * Constructor
46      *
47      * @param device the device to control
48      * @param eventListener listener to used ot report errors.
49      */
50     public DSMRDeviceRunnable(final DSMRDevice device, final P1TelegramListener eventListener) {
51         this.device = device;
52         this.portEventListener = eventListener;
53     }
54
55     /**
56      * Sets state to restart the dsmr device.
57      */
58     public void restart() {
59         releaseSemaphore();
60     }
61
62     /**
63      * Sets state to shutdown the dsmr device.
64      */
65     public void stop() {
66         running = false;
67         releaseSemaphore();
68     }
69
70     /**
71      * Controls the dsmr device. Runs until shutdown.
72      */
73     @Override
74     public void run() {
75         try {
76             running = true;
77             device.start();
78             while (running && !Thread.interrupted()) {
79                 semaphore.acquire();
80                 // Just drain all other permits to make sure it's not called twice
81                 semaphore.drainPermits();
82                 if (running) {
83                     logger.trace("Restarting device");
84                     device.restart();
85                 }
86             }
87             logger.trace("Device shutdown");
88         } catch (final RuntimeException e) {
89             logger.warn("DSMRDeviceRunnable stopped with a RuntimeException", e);
90             portEventListener.onError(DSMRErrorStatus.SERIAL_DATA_READ_ERROR,
91                     Optional.ofNullable(e.getMessage()).orElse(""));
92         } catch (final InterruptedException e) {
93             Thread.currentThread().interrupt();
94         } finally {
95             device.stop();
96         }
97     }
98
99     /**
100      * Wrapper around semaphore to only release when no permits available.
101      */
102     private void releaseSemaphore() {
103         synchronized (semaphore) {
104             if (semaphore.availablePermits() == 0) {
105                 semaphore.release();
106             }
107         }
108     }
109 }