|
|
|
@ -36,8 +36,11 @@ import org.thingsboard.server.common.data.query.EntityListFilter; |
|
|
|
import javax.net.ssl.SSLParameters; |
|
|
|
import java.net.URI; |
|
|
|
import java.nio.channels.NotYetConnectedException; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.concurrent.CountDownLatch; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
@ -48,13 +51,13 @@ import java.util.stream.Collectors; |
|
|
|
@Slf4j |
|
|
|
public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
|
|
|
|
public volatile JsonNode lastMsg; |
|
|
|
public final List<JsonNode> lastMsgs = new ArrayList<>(); |
|
|
|
private CountDownLatch reply; |
|
|
|
private CountDownLatch update; |
|
|
|
|
|
|
|
private final Lock updateLock = new ReentrantLock(); |
|
|
|
|
|
|
|
private long requestTimeoutMs; |
|
|
|
private final long requestTimeoutMs; |
|
|
|
|
|
|
|
public WsClient(URI serverUri, long requestTimeoutMs) { |
|
|
|
super(serverUri); |
|
|
|
@ -63,7 +66,6 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onOpen(ServerHandshake serverHandshake) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@ -73,8 +75,9 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
} |
|
|
|
updateLock.lock(); |
|
|
|
try { |
|
|
|
lastMsg = JacksonUtil.toJsonNode(s); |
|
|
|
log.trace("Received new msg: {}", lastMsg.toPrettyString()); |
|
|
|
JsonNode msg = JacksonUtil.toJsonNode(s); |
|
|
|
lastMsgs.add(msg); |
|
|
|
log.trace("Received new msg: {}", msg.toPrettyString()); |
|
|
|
if (update != null) { |
|
|
|
update.countDown(); |
|
|
|
} |
|
|
|
@ -96,11 +99,11 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
log.error("WebSocket client error:", e); |
|
|
|
} |
|
|
|
|
|
|
|
public void registerWaitForUpdate() { |
|
|
|
public void registerWaitForUpdates(int count) { |
|
|
|
updateLock.lock(); |
|
|
|
try { |
|
|
|
lastMsg = null; |
|
|
|
update = new CountDownLatch(1); |
|
|
|
lastMsgs.clear(); |
|
|
|
update = new CountDownLatch(count); |
|
|
|
} finally { |
|
|
|
updateLock.unlock(); |
|
|
|
} |
|
|
|
@ -111,6 +114,7 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
public void send(String text) throws NotYetConnectedException { |
|
|
|
updateLock.lock(); |
|
|
|
try { |
|
|
|
lastMsgs.clear(); |
|
|
|
reply = new CountDownLatch(1); |
|
|
|
} finally { |
|
|
|
updateLock.unlock(); |
|
|
|
@ -118,19 +122,19 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
super.send(text); |
|
|
|
} |
|
|
|
|
|
|
|
public WsClient subscribeForTelemetry(List<UUID> devices, String key) { |
|
|
|
public WsClient subscribeForTelemetry(List<UUID> devices, List<String> keys) { |
|
|
|
EntityDataCmd cmd = new EntityDataCmd(); |
|
|
|
cmd.setCmdId(RandomUtils.nextInt(0, 1000)); |
|
|
|
|
|
|
|
EntityListFilter devicesFilter = new EntityListFilter(); |
|
|
|
devicesFilter.setEntityType(EntityType.DEVICE); |
|
|
|
devicesFilter.setEntityList(devices.stream().map(UUID::toString).collect(Collectors.toList())); |
|
|
|
EntityDataPageLink pageLink = new EntityDataPageLink(100,0, null, null); |
|
|
|
EntityDataPageLink pageLink = new EntityDataPageLink(100, 0, null, null); |
|
|
|
EntityDataQuery devicesQuery = new EntityDataQuery(devicesFilter, pageLink, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); |
|
|
|
cmd.setQuery(devicesQuery); |
|
|
|
|
|
|
|
LatestValueCmd latestCmd = new LatestValueCmd(); |
|
|
|
latestCmd.setKeys(List.of(new EntityKey(EntityKeyType.TIME_SERIES, key))); |
|
|
|
latestCmd.setKeys(keys.stream().map(key -> new EntityKey(EntityKeyType.TIME_SERIES, key)).toList()); |
|
|
|
cmd.setLatestCmd(latestCmd); |
|
|
|
|
|
|
|
CmdsWrapper wrapper = new CmdsWrapper(); |
|
|
|
@ -139,12 +143,12 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
public JsonNode waitForUpdate(long ms) { |
|
|
|
public List<JsonNode> waitForUpdates(long ms) { |
|
|
|
log.trace("update latch count: {}", update.getCount()); |
|
|
|
try { |
|
|
|
if (update.await(ms, TimeUnit.MILLISECONDS)) { |
|
|
|
log.trace("Waited for update"); |
|
|
|
return getLastMsg(); |
|
|
|
return getLastMsgs(); |
|
|
|
} |
|
|
|
} catch (InterruptedException e) { |
|
|
|
log.debug("Failed to await reply", e); |
|
|
|
@ -157,7 +161,8 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
try { |
|
|
|
if (reply.await(requestTimeoutMs, TimeUnit.MILLISECONDS)) { |
|
|
|
log.trace("Waited for reply"); |
|
|
|
return getLastMsg(); |
|
|
|
List<JsonNode> lastMsgs = getLastMsgs(); |
|
|
|
return lastMsgs.isEmpty() ? null : lastMsgs.get(0); |
|
|
|
} |
|
|
|
} catch (InterruptedException e) { |
|
|
|
log.debug("Failed to await reply", e); |
|
|
|
@ -166,24 +171,30 @@ public class WsClient extends WebSocketClient implements AutoCloseable { |
|
|
|
throw new IllegalStateException("No WS reply arrived within " + requestTimeoutMs + " ms"); |
|
|
|
} |
|
|
|
|
|
|
|
private JsonNode getLastMsg() { |
|
|
|
if (lastMsg != null) { |
|
|
|
JsonNode errorMsg = lastMsg.get("errorMsg"); |
|
|
|
if (errorMsg != null && !errorMsg.isNull() && StringUtils.isNotEmpty(errorMsg.asText())) { |
|
|
|
throw new RuntimeException("WS error from server: " + errorMsg.asText()); |
|
|
|
} else { |
|
|
|
return lastMsg; |
|
|
|
} |
|
|
|
} else { |
|
|
|
return null; |
|
|
|
private List<JsonNode> getLastMsgs() { |
|
|
|
if (lastMsgs.isEmpty()) { |
|
|
|
return lastMsgs; |
|
|
|
} |
|
|
|
List<JsonNode> errors = lastMsgs.stream() |
|
|
|
.map(msg -> msg.get("errorMsg")) |
|
|
|
.filter(errorMsg -> errorMsg != null && !errorMsg.isNull() && StringUtils.isNotEmpty(errorMsg.asText())) |
|
|
|
.toList(); |
|
|
|
if (!errors.isEmpty()) { |
|
|
|
throw new RuntimeException("WS error from server: " + errors.stream() |
|
|
|
.map(JsonNode::asText) |
|
|
|
.collect(Collectors.joining(", "))); |
|
|
|
} |
|
|
|
return lastMsgs; |
|
|
|
} |
|
|
|
|
|
|
|
public Object getTelemetryUpdate(UUID deviceId, String key) { |
|
|
|
JsonNode lastMsg = getLastMsg(); |
|
|
|
if (lastMsg == null || lastMsg.isNull()) return null; |
|
|
|
EntityDataUpdate update = JacksonUtil.treeToValue(lastMsg, EntityDataUpdate.class); |
|
|
|
return update.getLatest(deviceId, key); |
|
|
|
public Map<String, String> getLatest(UUID deviceId) { |
|
|
|
Map<String, String> updates = new HashMap<>(); |
|
|
|
getLastMsgs().forEach(msg -> { |
|
|
|
EntityDataUpdate update = JacksonUtil.treeToValue(msg, EntityDataUpdate.class); |
|
|
|
Map<String, String> latest = update.getLatest(deviceId); |
|
|
|
updates.putAll(latest); |
|
|
|
}); |
|
|
|
return updates; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
|