Skip to content

Commit aa25d7e

Browse files
committed
test(haywardomnilogiclocal): verify ack handling
1 parent 308ea64 commit aa25d7e

2 files changed

Lines changed: 142 additions & 0 deletions

File tree

  • bundles/org.openhab.binding.haywardomnilogiclocal/src
    • main/java/org/openhab/binding/haywardomnilogiclocal/internal/net
    • test/java/org/openhab/binding/haywardomnilogiclocal/internal/net

bundles/org.openhab.binding.haywardomnilogiclocal/src/main/java/org/openhab/binding/haywardomnilogiclocal/internal/net/UdpClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ public UdpResponse send(UdpRequest request) throws IOException {
110110
response = UdpResponse.fromBytes(packetBytes, packetBytes.length);
111111
break;
112112
}
113+
} else if (msgType == HaywardMessageType.ACK.getMsgInt()) {
114+
continue;
113115
} else {
114116
response = UdpResponse.fromBytes(data, data.length);
115117
break;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package org.openhab.binding.haywardomnilogiclocal.internal.net;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.net.DatagramPacket;
8+
import java.net.DatagramSocket;
9+
import java.net.InetAddress;
10+
import java.net.SocketTimeoutException;
11+
import java.nio.ByteBuffer;
12+
import java.nio.charset.StandardCharsets;
13+
import java.util.concurrent.atomic.AtomicBoolean;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.junit.jupiter.api.Test;
17+
import org.openhab.binding.haywardomnilogiclocal.internal.HaywardMessageType;
18+
19+
/**
20+
* Tests for {@link UdpClient}.
21+
*/
22+
@NonNullByDefault
23+
public class UdpClientTest {
24+
25+
@Test
26+
public void sendShouldIgnoreAckAndProcessSubsequentData() throws Exception {
27+
final DatagramSocket server = new DatagramSocket(0);
28+
final int port = server.getLocalPort();
29+
final AtomicBoolean ackBeforeLead = new AtomicBoolean(false);
30+
final AtomicBoolean ackAfterLead = new AtomicBoolean(false);
31+
final String responseXml = "<Response>OK</Response>";
32+
33+
Thread serverThread = new Thread(() -> {
34+
try {
35+
byte[] buf = new byte[4096];
36+
DatagramPacket requestPacket = new DatagramPacket(buf, buf.length);
37+
server.receive(requestPacket);
38+
ByteBuffer reqBuffer = ByteBuffer.wrap(requestPacket.getData(), 0, requestPacket.getLength());
39+
int msgId = reqBuffer.getInt();
40+
41+
InetAddress clientAddress = requestPacket.getAddress();
42+
int clientPort = requestPacket.getPort();
43+
44+
// initial ACK from server
45+
byte[] ackBytes = createAckPacket(msgId);
46+
server.send(new DatagramPacket(ackBytes, ackBytes.length, clientAddress, clientPort));
47+
48+
// verify no ACK was sent from client yet
49+
server.setSoTimeout(500);
50+
try {
51+
DatagramPacket unexpected = new DatagramPacket(new byte[4096], 4096);
52+
server.receive(unexpected);
53+
ackBeforeLead.set(true);
54+
} catch (SocketTimeoutException e) {
55+
// expected - no ACK before lead
56+
}
57+
58+
// send lead block message
59+
byte[] leadBytes = createLeadPacket(msgId, 1, false);
60+
server.send(new DatagramPacket(leadBytes, leadBytes.length, clientAddress, clientPort));
61+
62+
// client should ACK after receiving lead
63+
server.setSoTimeout(2000);
64+
try {
65+
DatagramPacket ackFromClient = new DatagramPacket(new byte[4096], 4096);
66+
server.receive(ackFromClient);
67+
ByteBuffer ackBuf = ByteBuffer.wrap(ackFromClient.getData(), 0, ackFromClient.getLength());
68+
int msgType = ackBuf.getInt(16);
69+
if (msgType == HaywardMessageType.ACK.getMsgInt()) {
70+
ackAfterLead.set(true);
71+
}
72+
// consume possible second ACK
73+
server.setSoTimeout(100);
74+
try {
75+
server.receive(new DatagramPacket(new byte[4096], 4096));
76+
} catch (SocketTimeoutException ex) {
77+
// ignore
78+
}
79+
} catch (SocketTimeoutException e) {
80+
// no ACK received
81+
}
82+
83+
// send block containing XML payload
84+
byte[] blockBytes = createBlockPacket(msgId, responseXml);
85+
server.send(new DatagramPacket(blockBytes, blockBytes.length, clientAddress, clientPort));
86+
} catch (Exception e) {
87+
// ignore for test
88+
}
89+
});
90+
serverThread.start();
91+
92+
UdpClient client = new UdpClient("127.0.0.1", port);
93+
UdpRequest request = new UdpRequest(HaywardMessageType.GET_TELEMETRY.getMsgInt(), "<Request/>");
94+
UdpResponse response = client.send(request);
95+
96+
serverThread.join();
97+
server.close();
98+
99+
assertEquals(HaywardMessageType.MSP_BLOCKMESSAGE.getMsgInt(), response.getMessageType());
100+
assertEquals(responseXml, response.getXml());
101+
assertFalse(ackBeforeLead.get());
102+
assertTrue(ackAfterLead.get());
103+
}
104+
105+
private static byte[] createAckPacket(int messageId) throws Exception {
106+
UdpRequest ack = new UdpRequest(HaywardMessageType.ACK.getMsgInt(), "ACK", messageId);
107+
return ack.toBytes();
108+
}
109+
110+
private static byte[] createLeadPacket(int messageId, int blocks, boolean compressed) {
111+
ByteBuffer buffer = ByteBuffer.allocate(24);
112+
buffer.putInt(messageId);
113+
buffer.putLong(System.currentTimeMillis());
114+
buffer.put("1.22".getBytes(StandardCharsets.US_ASCII));
115+
buffer.putInt(HaywardMessageType.MSP_LEADMESSAGE.getMsgInt());
116+
buffer.put((byte) 1);
117+
buffer.put((byte) blocks);
118+
buffer.put((byte) (compressed ? 1 : 0));
119+
buffer.put((byte) 0);
120+
return buffer.array();
121+
}
122+
123+
private static byte[] createBlockPacket(int messageId, String xml) {
124+
byte[] xmlBytes = (xml + '\0').getBytes(StandardCharsets.UTF_8);
125+
ByteBuffer header = ByteBuffer.allocate(24);
126+
header.putInt(messageId);
127+
header.putLong(System.currentTimeMillis());
128+
header.put("1.22".getBytes(StandardCharsets.US_ASCII));
129+
header.putInt(HaywardMessageType.MSP_BLOCKMESSAGE.getMsgInt());
130+
header.put((byte) 1);
131+
header.put((byte) 0);
132+
header.put((byte) 0);
133+
header.put((byte) 0);
134+
byte[] packet = new byte[24 + xmlBytes.length];
135+
System.arraycopy(header.array(), 0, packet, 0, 24);
136+
System.arraycopy(xmlBytes, 0, packet, 24, xmlBytes.length);
137+
return packet;
138+
}
139+
}
140+

0 commit comments

Comments
 (0)