]> git.basschouten.com Git - openhab-addons.git/blob
85e9d9923ac975a4a2f676b01647e5054c7f9f85
[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.upb.internal;
14
15 import static java.nio.charset.StandardCharsets.US_ASCII;
16 import static org.junit.jupiter.api.Assertions.assertEquals;
17 import static org.junit.jupiter.api.Assertions.assertFalse;
18 import static org.junit.jupiter.api.Assertions.assertTrue;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.when;
21
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.io.OutputStreamWriter;
25 import java.io.PipedInputStream;
26 import java.io.PipedOutputStream;
27 import java.util.concurrent.BlockingQueue;
28 import java.util.concurrent.CompletableFuture;
29 import java.util.concurrent.CompletionStage;
30 import java.util.concurrent.LinkedBlockingQueue;
31
32 import org.junit.jupiter.api.AfterEach;
33 import org.junit.jupiter.api.BeforeEach;
34 import org.junit.jupiter.api.Test;
35 import org.mockito.Mock;
36 import org.openhab.binding.upb.internal.handler.MessageListener;
37 import org.openhab.binding.upb.internal.handler.SerialIoThread;
38 import org.openhab.binding.upb.internal.handler.UPBIoHandler.CmdStatus;
39 import org.openhab.binding.upb.internal.message.Command;
40 import org.openhab.binding.upb.internal.message.MessageBuilder;
41 import org.openhab.binding.upb.internal.message.UPBMessage;
42 import org.openhab.core.io.transport.serial.SerialPort;
43 import org.openhab.core.thing.ThingUID;
44
45 /**
46  * @author Marcus Better - Initial contribution
47  */
48 public class SerialIoThreadTest {
49
50     private static final String ENABLE_MESSAGE_MODE_CMD = "\u001770028E\n";
51
52     private final ThingUID thingUID = new ThingUID("a", "b", "c");
53     private final Listener msgListener = new Listener();
54     private final PipedOutputStream in = new PipedOutputStream();
55     private final OutputStreamWriter inbound = new OutputStreamWriter(in, US_ASCII);
56     private final PipedOutputStream out = new PipedOutputStream();
57
58     private @Mock SerialPort serialPort;
59     private SerialIoThread thread;
60     private InputStreamReader outbound;
61     final char[] buf = new char[256];
62
63     @BeforeEach
64     public void setup() throws IOException {
65         serialPort = mock(SerialPort.class);
66         outbound = new InputStreamReader(new PipedInputStream(out), US_ASCII);
67         when(serialPort.getInputStream()).thenReturn(new PipedInputStream(in));
68         when(serialPort.getOutputStream()).thenReturn(out);
69         thread = new SerialIoThread(serialPort, msgListener, thingUID);
70         thread.start();
71     }
72
73     @AfterEach
74     public void cleanup() {
75         thread.terminate();
76     }
77
78     @Test
79     public void testName() {
80         assertEquals("OH-binding-a:b:c-serial-reader", thread.getName());
81         assertTrue(thread.isDaemon());
82     }
83
84     @Test
85     public void receive() throws Exception {
86         writeInbound("PU8905FA011220FFFF47\r");
87         final UPBMessage msg = msgListener.readInbound();
88         assertEquals(Command.ACTIVATE, msg.getCommand());
89         assertEquals(1, msg.getDestination());
90         writeInbound("PU8905FA011221FFFF48\r");
91         final UPBMessage msg2 = msgListener.readInbound();
92         assertEquals(Command.DEACTIVATE, msg2.getCommand());
93         verifyMessageModeCmd();
94     }
95
96     @Test
97     public void send() throws Exception {
98         final String msg = MessageBuilder.forCommand(Command.GOTO).args((byte) 10).network((byte) 2)
99                 .destination((byte) 5).build();
100         final CompletionStage<CmdStatus> fut = thread.enqueue(msg);
101         verifyMessageModeCmd();
102         final int n = outbound.read(buf);
103         assertEquals("\u001408100205FF220AB6\r", new String(buf, 0, n));
104         ack();
105         final CmdStatus res = fut.toCompletableFuture().join();
106         assertEquals(CmdStatus.ACK, res);
107     }
108
109     @Test
110     public void resend() throws Exception {
111         final String msg = MessageBuilder.forCommand(Command.GOTO).args((byte) 10).network((byte) 2)
112                 .destination((byte) 5).build();
113         final CompletableFuture<CmdStatus> fut = thread.enqueue(msg).toCompletableFuture();
114         verifyMessageModeCmd();
115         int n = outbound.read(buf);
116         assertEquals("\u001408100205FF220AB6\r", new String(buf, 0, n));
117         nak();
118
119         // should re-send
120         n = outbound.read(buf);
121         assertEquals("\u001408100205FF220AB6\r", new String(buf, 0, n));
122         assertFalse(fut.isDone());
123         ack();
124         final CmdStatus res = fut.join();
125         assertEquals(CmdStatus.ACK, res);
126     }
127
128     @Test
129     public void resendMaxAttempts() throws Exception {
130         final String msg = MessageBuilder.forCommand(Command.GOTO).args((byte) 10).network((byte) 2)
131                 .destination((byte) 5).build();
132         final CompletableFuture<CmdStatus> fut = thread.enqueue(msg).toCompletableFuture();
133         verifyMessageModeCmd();
134         int n = outbound.read(buf);
135         assertEquals("\u001408100205FF220AB6\r", new String(buf, 0, n));
136         nak();
137
138         // retry
139         n = outbound.read(buf);
140         assertEquals("\u001408100205FF220AB6\r", new String(buf, 0, n));
141         assertFalse(fut.isDone());
142         // no response - wait for ack timeout
143
144         // last retry
145         n = outbound.read(buf);
146         assertEquals("\u001408100205FF220AB6\r", new String(buf, 0, n));
147         assertFalse(fut.isDone());
148         nak();
149         final CmdStatus res = fut.join();
150         assertEquals(CmdStatus.NAK, res);
151     }
152
153     private void ack() throws IOException {
154         writeInbound("PK\r");
155     }
156
157     private void nak() throws IOException {
158         writeInbound("PN\r");
159     }
160
161     private void writeInbound(String s) throws IOException {
162         inbound.write(s);
163         inbound.flush();
164     }
165
166     private void verifyMessageModeCmd() throws IOException {
167         final int n = outbound.read(buf, 0, ENABLE_MESSAGE_MODE_CMD.length());
168         assertEquals(ENABLE_MESSAGE_MODE_CMD, new String(buf, 0, n));
169     }
170
171     private static class Listener implements MessageListener {
172
173         private final BlockingQueue<UPBMessage> messages = new LinkedBlockingQueue<>();
174
175         @Override
176         public void incomingMessage(final UPBMessage msg) {
177             messages.offer(msg);
178         }
179
180         @Override
181         public void onError(final Throwable t) {
182         }
183
184         public UPBMessage readInbound() {
185             try {
186                 return messages.take();
187             } catch (InterruptedException e) {
188                 return null;
189             }
190         }
191     }
192 }