]> git.basschouten.com Git - openhab-addons.git/blob
04c338ca54fa883d45234a36c8265e6d4f51c66c
[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.pjlinkdevice.internal.discovery;
14
15 import java.net.InetAddress;
16 import java.net.InterfaceAddress;
17 import java.net.NetworkInterface;
18 import java.net.SocketException;
19 import java.util.ArrayList;
20 import java.util.HashSet;
21 import java.util.Set;
22 import java.util.concurrent.ExecutorService;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.TimeUnit;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.pjlinkdevice.internal.PJLinkDeviceBindingConstants;
29 import org.openhab.core.config.discovery.AbstractDiscoveryService;
30 import org.openhab.core.thing.ThingTypeUID;
31 import org.openhab.core.thing.ThingUID;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * Discovery of PJLink devices. Checks IP addresses in parallel processing.
37  * 
38  * Generating IP addresses and checking them is done by the subclasses implementing
39  * {@link AbstractDiscoveryParticipant#generateAddressesToScan} and {@link AbstractDiscoveryParticipant#checkAddress}
40  * 
41  * @author Nils Schnabel - Initial contribution
42  */
43 @NonNullByDefault
44 public abstract class AbstractDiscoveryParticipant extends AbstractDiscoveryService {
45     protected final Logger logger = LoggerFactory.getLogger(AbstractDiscoveryParticipant.class);
46     private Integer scannedIPcount = 0;
47     private @Nullable ExecutorService executorService = null;
48
49     public AbstractDiscoveryParticipant(Set<ThingTypeUID> supportedThingTypes, int timeout,
50             boolean backgroundDiscoveryEnabledByDefault) throws IllegalArgumentException {
51         super(supportedThingTypes, timeout, backgroundDiscoveryEnabledByDefault);
52     }
53
54     protected ExecutorService getExecutorService() {
55         ExecutorService executorService = this.executorService;
56         if (executorService == null) {
57             this.executorService = executorService = Executors
58                     .newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
59         }
60         return executorService;
61     }
62
63     @Override
64     protected void startScan() {
65         logger.trace("PJLinkProjectorDiscoveryParticipant startScan");
66         Set<InetAddress> addressesToScan = generateAddressesToScan();
67         scannedIPcount = 0;
68         for (InetAddress ip : addressesToScan) {
69             getExecutorService().execute(() -> {
70                 Thread.currentThread().setName("Discovery thread " + ip);
71                 checkAddress(ip, PJLinkDeviceBindingConstants.DEFAULT_PORT,
72                         PJLinkDeviceBindingConstants.DEFAULT_SCAN_TIMEOUT_SECONDS);
73
74                 synchronized (scannedIPcount) {
75                     scannedIPcount += 1;
76                     logger.debug("Scanned {} of {} IPs", scannedIPcount, addressesToScan.size());
77                     if (scannedIPcount == addressesToScan.size()) {
78                         logger.debug("Scan of {} IPs successful", scannedIPcount);
79                         stopScan();
80                     }
81                 }
82             });
83         }
84     }
85
86     @Override
87     protected synchronized void stopScan() {
88         super.stopScan();
89         ExecutorService executorService = this.executorService;
90         if (executorService == null) {
91             return;
92         }
93
94         try {
95             executorService.awaitTermination(10000, TimeUnit.MILLISECONDS);
96         } catch (InterruptedException e) {
97             Thread.currentThread().interrupt(); // Reset interrupt flag
98         }
99         executorService.shutdown();
100         this.executorService = null;
101     }
102
103     public static ThingUID createServiceUID(String ip, int tcpPort) {
104         // uid must not contains dots
105         return new ThingUID(PJLinkDeviceBindingConstants.THING_TYPE_PJLINK, ip.replace('.', '_') + "_" + tcpPort);
106     }
107
108     protected abstract void checkAddress(InetAddress ip, int tcpPort, int timeout);
109
110     private Set<InetAddress> generateAddressesToScan() {
111         try {
112             Set<InetAddress> addressesToScan = new HashSet<>();
113             ArrayList<NetworkInterface> interfaces = java.util.Collections
114                     .list(NetworkInterface.getNetworkInterfaces());
115             for (NetworkInterface networkInterface : interfaces) {
116                 if (networkInterface.isLoopback() || !networkInterface.isUp()) {
117                     continue;
118                 }
119                 for (InterfaceAddress i : networkInterface.getInterfaceAddresses()) {
120                     collectAddressesToScan(addressesToScan, i);
121                 }
122             }
123             return addressesToScan;
124         } catch (SocketException e) {
125             logger.debug("Could not enumerate network interfaces", e);
126         }
127         return new HashSet<>();
128     }
129
130     protected abstract void collectAddressesToScan(Set<InetAddress> addressesToScan, InterfaceAddress i);
131 }