]> git.basschouten.com Git - openhab-addons.git/blob
8109653f638ade8fbb24a195f59cd1f8ee71a9d1
[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.echonetlite.internal;
14
15 import java.io.IOException;
16 import java.net.Inet4Address;
17 import java.net.InetSocketAddress;
18 import java.net.NetworkInterface;
19 import java.net.SocketAddress;
20 import java.net.StandardProtocolFamily;
21 import java.nio.ByteBuffer;
22 import java.nio.channels.DatagramChannel;
23 import java.nio.channels.SelectionKey;
24 import java.nio.channels.Selector;
25 import java.util.Enumeration;
26 import java.util.function.BiConsumer;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * Wraps a Datagram channel for sending/receiving data to/from echonet lite devices.
34  *
35  * @author Michael Barker - Initial contribution
36  */
37 @NonNullByDefault
38 public class EchonetChannel {
39
40     private final Logger logger = LoggerFactory.getLogger(EchonetChannel.class);
41
42     private final DatagramChannel channel;
43     private final Selector selector = Selector.open();
44
45     private short tid = 0;
46
47     public EchonetChannel(InetSocketAddress discoveryAddress) throws IOException {
48         channel = DatagramChannel.open(StandardProtocolFamily.INET);
49         channel.bind(new InetSocketAddress("0.0.0.0", discoveryAddress.getPort()));
50         final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
51         while (networkInterfaces.hasMoreElements()) {
52             final NetworkInterface networkInterface = (NetworkInterface) networkInterfaces.nextElement();
53             if (networkInterface.supportsMulticast() && hasIpV4Address(networkInterface)) {
54                 channel.join(discoveryAddress.getAddress(), networkInterface);
55             }
56         }
57         channel.configureBlocking(false);
58         channel.register(selector, SelectionKey.OP_READ);
59     }
60
61     private boolean hasIpV4Address(final NetworkInterface networkInterface) {
62         return networkInterface.inetAddresses().anyMatch(ia -> ia instanceof Inet4Address);
63     }
64
65     public void close() {
66         try {
67             logger.debug("closing selector");
68             selector.close();
69             logger.debug("closing channel");
70             channel.close();
71         } catch (IOException ignore) {
72         }
73     }
74
75     short nextTid() {
76         return tid++;
77     }
78
79     public void sendMessage(EchonetMessageBuilder messageBuilder) throws IOException {
80         messageBuilder.buffer().flip();
81         channel.send(messageBuilder.buffer(), messageBuilder.address());
82     }
83
84     public void pollMessages(EchonetMessage echonetMessage, BiConsumer<EchonetMessage, SocketAddress> consumer,
85             final long timeout) throws IOException {
86         selector.select(selectionKey -> {
87             final DatagramChannel channel = (DatagramChannel) selectionKey.channel();
88             try {
89                 final ByteBuffer buffer = echonetMessage.bufferForRead();
90                 final SocketAddress address = channel.receive(buffer);
91
92                 echonetMessage.sourceAddress(address);
93                 buffer.flip();
94                 long t0 = System.currentTimeMillis();
95                 consumer.accept(echonetMessage, address);
96                 long t1 = System.currentTimeMillis();
97                 final long processingTimeMs = t1 - t0;
98                 if (500 < processingTimeMs) {
99                     logger.debug("Message took {}ms to process", processingTimeMs);
100                 }
101             } catch (IOException e) {
102                 logger.warn("Failed to receive on channel", e);
103             }
104         }, timeout);
105     }
106 }