package eu.hgross.blaubot.util; import com.google.gson.Gson; import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import eu.hgross.blaubot.core.BlaubotConstants; import eu.hgross.blaubot.core.IBlaubotDevice; import eu.hgross.blaubot.messaging.BlaubotMessage; import eu.hgross.blaubot.messaging.IBlaubotChannel; import eu.hgross.blaubot.messaging.IBlaubotMessageListener; import eu.hgross.blaubot.ui.PingMessage; /** * Sends n PingMessages, awaits their echo and calculates the min,max,avg values. */ public class PingMeasurer { private static final String LOG_TAG = "PingMeasurer"; /** * Max amount of ms for one ping message before we time out */ private static final long PING_MEASURE_TIMEOUT = 25000; private final IBlaubotChannel mChannel; private final IBlaubotDevice mOwnDevice; private final Gson mGson = new Gson(); public PingMeasurer(IBlaubotChannel channel, IBlaubotDevice ownDevice) { this.mChannel = channel; this.mOwnDevice = ownDevice; } /** * Sends one ping message * * @return number of bytes of the ping message */ private int sendPingMessage() { if (Log.logDebugMessages()) { Log.d(LOG_TAG, "Sending ping ..."); } PingMessage pingMessage = new PingMessage(); pingMessage.setSenderUniqueDeviceId(mOwnDevice.getUniqueDeviceID()); pingMessage.setTimestamp(System.currentTimeMillis()); String serialized = mGson.toJson(pingMessage); final byte[] bytes = serialized.getBytes(BlaubotConstants.STRING_CHARSET); mChannel.publish(bytes); return bytes.length; } /** * Measure the RTT by sending n pings and awaitng the echos. * * @param n the number of pings to send * @return the result of the RTT measurement */ public Future<PingMeasurerResult> measure(final int n) { FutureTask<PingMeasurerResult> future = new FutureTask<PingMeasurerResult>(new Callable<PingMeasurerResult>() { long bytesSent = 0; long min = -1; long max = -1; long sum = 0; int i = 0; public PingMeasurerResult call() { if (Log.logDebugMessages()) { Log.d(LOG_TAG, "Measuring RTT with " + n + " messages ..."); } final CountDownLatch latch = new CountDownLatch(n); mChannel.addMessageListener(new IBlaubotMessageListener() { @Override public void onMessage(BlaubotMessage blaubotMessage) { final Date receivedDate = new Date(); final String msg = new String(blaubotMessage.getPayload(), BlaubotConstants.STRING_CHARSET); final PingMessage pingMessage = mGson.fromJson(msg, PingMessage.class); long rtt = receivedDate.getTime() - pingMessage.getTimestamp(); if (Log.logDebugMessages()) { Log.d(LOG_TAG, "Got ping (RTT=" + rtt + ")"); } if (min < 0 || rtt < min) { min = rtt; } if (max < 0 || rtt > max) { max = rtt; } sum += rtt; i += 1; if (n - i > 0) { // we need to send another bytesSent += sendPingMessage(); } latch.countDown(); } }); if (n - i > 0) { bytesSent += sendPingMessage(); } try { final boolean timedOut = !latch.await(PING_MEASURE_TIMEOUT * n, TimeUnit.MILLISECONDS); if (timedOut) { if (Log.logWarningMessages()) { Log.w(LOG_TAG, "PingMeasurement could not be completed (timeout of " + PING_MEASURE_TIMEOUT * n + " ms occured first)"); } } } catch (InterruptedException e) { if (Log.logWarningMessages()) { Log.w(LOG_TAG, "PingMeasurement has been interrupted"); } } final float avg = ((float) sum) / ((float) i); return new PingMeasurerResult(min, max, avg, bytesSent, i); } }); new Thread(future).start(); return future; } }