2 * Copyright (c) 2010-2023 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.digitalstrom.internal.lib.sensorjobexecutor;
15 import java.util.HashMap;
16 import java.util.LinkedList;
17 import java.util.List;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
23 import org.openhab.binding.digitalstrom.internal.lib.config.Config;
24 import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
25 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
26 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
27 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
28 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
29 import org.openhab.core.common.ThreadPoolManager;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
34 * The {@link AbstractSensorJobExecutor} provides the working process to execute implementations of {@link SensorJob}'s
35 * in the time interval set at the {@link Config}.
37 * The following methods can be overridden by subclasses to implement an execution priority:
40 * <li>{@link #addLowPriorityJob(SensorJob)}</li>
41 * <li>{@link #addMediumPriorityJob(SensorJob)}</li>
42 * <li>{@link #addHighPriorityJob(SensorJob)}</li>
45 * @author Michael Ochel - Initial contribution
46 * @author Matthias Siegele - Initial contribution
49 public abstract class AbstractSensorJobExecutor {
51 private final Logger logger = LoggerFactory.getLogger(AbstractSensorJobExecutor.class);
53 private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME);
54 private Map<DSID, ScheduledFuture<?>> pollingSchedulers;
56 private final DsAPI dSAPI;
57 protected Config config;
58 private final ConnectionManager connectionManager;
60 private final List<CircuitScheduler> circuitSchedulerList = new LinkedList<>();
62 private class ExecutorRunnable implements Runnable {
63 private final CircuitScheduler circuit;
65 public ExecutorRunnable(CircuitScheduler circuit) {
66 this.circuit = circuit;
71 // pollingSchedulers is not final and might be set to null by another thread. See #8214
72 Map<DSID, ScheduledFuture<?>> pollingSchedulers = AbstractSensorJobExecutor.this.pollingSchedulers;
74 SensorJob sensorJob = circuit.getNextSensorJob();
75 DSID meter = circuit.getMeterDSID();
77 if (sensorJob != null) {
78 sensorJob.execute(dSAPI, connectionManager.getSessionToken());
80 if (circuit.noMoreJobs() && pollingSchedulers != null) {
81 logger.debug("no more jobs... stop circuit schedduler with id = {}", meter);
82 ScheduledFuture<?> scheduler = pollingSchedulers.get(meter);
83 if (scheduler != null) {
84 scheduler.cancel(true);
91 * Creates a new {@link AbstractSensorJobExecutor}.
93 * @param connectionManager must not be null
95 public AbstractSensorJobExecutor(ConnectionManager connectionManager) {
96 this.connectionManager = connectionManager;
97 config = connectionManager.getConfig();
98 this.dSAPI = connectionManager.getDigitalSTROMAPI();
102 * Stops all circuit schedulers.
104 public synchronized void shutdown() {
105 if (pollingSchedulers != null) {
106 for (ScheduledFuture<?> scheduledExecutor : pollingSchedulers.values()) {
107 scheduledExecutor.cancel(true);
109 pollingSchedulers = null;
110 logger.debug("stop all circuit schedulers.");
115 * Starts all circuit schedulers.
117 public synchronized void startExecutor() {
118 logger.debug("start all circuit schedulers.");
119 if (pollingSchedulers == null) {
120 pollingSchedulers = new HashMap<>();
122 if (circuitSchedulerList != null && !circuitSchedulerList.isEmpty()) {
123 for (CircuitScheduler circuit : circuitSchedulerList) {
124 startSchedduler(circuit);
129 private void startSchedduler(CircuitScheduler circuit) {
130 if (pollingSchedulers != null) {
131 if (pollingSchedulers.get(circuit.getMeterDSID()) == null
132 || pollingSchedulers.get(circuit.getMeterDSID()).isCancelled()) {
133 pollingSchedulers.put(circuit.getMeterDSID(),
134 scheduler.scheduleWithFixedDelay(new ExecutorRunnable(circuit), circuit.getNextExecutionDelay(),
135 config.getSensorReadingWaitTime(), TimeUnit.MILLISECONDS));
141 * Adds a high priority {@link SensorJob}.
143 * @param sensorJob to add
145 public void addHighPriorityJob(SensorJob sensorJob) {
146 // can be Overridden to implement a priority
147 addSensorJobToCircuitScheduler(sensorJob);
151 * Adds a medium priority {@link SensorJob}.
153 * @param sensorJob to add
155 public void addMediumPriorityJob(SensorJob sensorJob) {
156 // can be overridden to implement a priority
157 addSensorJobToCircuitScheduler(sensorJob);
161 * Adds a low priority {@link SensorJob}.
163 * @param sensorJob to add
165 public void addLowPriorityJob(SensorJob sensorJob) {
166 // can be overridden to implement a priority
167 addSensorJobToCircuitScheduler(sensorJob);
171 * Adds a {@link SensorJob} with a given priority .
173 * @param sensorJob to add
174 * @param priority to update
176 public void addPriorityJob(SensorJob sensorJob, long priority) {
177 if (sensorJob == null) {
180 sensorJob.setInitalisationTime(priority);
181 addSensorJobToCircuitScheduler(sensorJob);
182 logger.debug("Add SensorJob from device with dSID {} and priority {} to AbstractJobExecutor",
183 sensorJob.getDSID(), priority);
187 * Adds the given {@link SensorJob}.
189 * @param sensorJob to add
191 protected void addSensorJobToCircuitScheduler(SensorJob sensorJob) {
192 synchronized (this.circuitSchedulerList) {
193 CircuitScheduler circuit = getCircuitScheduler(sensorJob.getMeterDSID());
194 if (circuit != null) {
195 circuit.addSensorJob(sensorJob);
197 circuit = new CircuitScheduler(sensorJob, config);
198 this.circuitSchedulerList.add(circuit);
200 startSchedduler(circuit);
204 private CircuitScheduler getCircuitScheduler(DSID dsid) {
205 for (CircuitScheduler circuit : this.circuitSchedulerList) {
206 if (circuit.getMeterDSID().equals(dsid)) {
214 * Removes all SensorJobs of a specific {@link Device}.
216 * @param device to remove
218 public void removeSensorJobs(Device device) {
219 if (device != null) {
220 CircuitScheduler circuit = getCircuitScheduler(device.getMeterDSID());
221 if (circuit != null) {
222 circuit.removeSensorJob(device.getDSID());
228 * Removes the {@link SensorJob} with the given ID.
230 * @param device needed for the meterDSID
231 * @param ID of the {@link SensorJob} to remove
233 public void removeSensorJob(Device device, String ID) {
234 if (device != null && ID != null) {
235 CircuitScheduler circuit = getCircuitScheduler(device.getMeterDSID());
236 if (circuit != null) {
237 circuit.removeSensorJob(ID);