/*******************************************************************************
* Copyright 2013-2016 alladin-IT GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package at.alladin.rmbt.client;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.json.JSONObject;
import at.alladin.rmbt.client.RMBTTest.CurrentSpeed;
import at.alladin.rmbt.client.helper.Config;
import at.alladin.rmbt.client.helper.ControlServerConnection;
import at.alladin.rmbt.client.helper.IntermediateResult;
import at.alladin.rmbt.client.helper.RMBTOutputCallback;
import at.alladin.rmbt.client.helper.TestStatus;
import at.alladin.rmbt.client.v2.task.TaskDesc;
import at.alladin.rmbt.client.v2.task.result.QoSResultCollector;
import at.alladin.rmbt.client.v2.task.service.TestMeasurement;
import at.alladin.rmbt.client.v2.task.service.TrafficService;
import at.alladin.rmbt.util.model.shared.exception.ErrorStatus;
public class RMBTClient
{
private static final ExecutorService COMMON_THREAD_POOL = Executors.newCachedThreadPool();
private final RMBTTestParameter params;
private final long durationInitNano = 2500000000L; // TODO
private final long durationUpNano;
private final long durationDownNano;
private final AtomicLong pingNano = new AtomicLong(-1);
private final AtomicLong downBitPerSec = new AtomicLong(-1);
private final AtomicLong upBitPerSec = new AtomicLong(-1);
/* ping status */
private final AtomicLong pingTsStart = new AtomicLong(-1);
private final AtomicInteger pingNumDome = new AtomicInteger(-1);
private final AtomicLong pingTsLastPing = new AtomicLong(-1);
private final static long MIN_DIFF_TIME = 100000000; // 100 ms
private final static int KEEP_LAST_ENTRIES = 20;
private int lastCounter;
private final long[][] lastTransfer;
private final long[][] lastTime;
private final ExecutorService testThreadPool;
private final RMBTTest[] testTasks;
private TotalTestResult result;
private SSLSocketFactory sslSocketFactory;
private RMBTOutputCallback outputCallback;
private final boolean outputToStdout = true;
private final ControlServerConnection controlConnection;
private final AtomicBoolean aborted = new AtomicBoolean();
private String errorMsg = "";
/*------------------------------------
V2 tests
--------------------------------------*/
public final static String TASK_UDP = "udp";
public final static String TASK_TCP = "tcp";
public final static String TASK_DNS = "dns";
public final static String TASK_VOIP = "voip";
public final static String TASK_NON_TRANSPARENT_PROXY = "non_transparent_proxy";
public final static String TASK_HTTP = "http_proxy";
public final static String TASK_WEBSITE = "website";
public final static String TASK_TRACEROUTE = "traceroute";
private List<TaskDesc> taskDescList;
/*------------------------------------*/
private final AtomicReference<TestStatus> testStatus = new AtomicReference<TestStatus>(TestStatus.WAIT);
private final AtomicReference<TestStatus> statusBeforeError = new AtomicReference<TestStatus>(null);
private final AtomicLong statusChangeTime = new AtomicLong();
private TrafficService trafficService;
public static ExecutorService getCommonThreadPool()
{
return COMMON_THREAD_POOL;
}
private ConcurrentHashMap<TestStatus, TestMeasurement> measurementMap = new ConcurrentHashMap<TestStatus, TestMeasurement>();
public static RMBTClient getInstance(final String host, final String pathPrefix, final int port,
final boolean encryption, final ArrayList<String> geoInfo, final String uuid, final String clientType,
final String clientName, final String clientVersion, final RMBTTestParameter overrideParams,
final JSONObject additionalValues) {
return getInstance(host, pathPrefix, port, encryption, geoInfo, uuid, clientType,
clientName, clientVersion, overrideParams, additionalValues, null);
}
public static RMBTClient getInstance(final String host, final String pathPrefix, final int port,
final boolean encryption, final ArrayList<String> geoInfo, final String uuid, final String clientType,
final String clientName, final String clientVersion, final RMBTTestParameter overrideParams,
final JSONObject additionalValues, final Set<ErrorStatus> errorSet)
{
final ControlServerConnection controlConnection = new ControlServerConnection();
final String error = controlConnection.requestNewTestConnection(host, pathPrefix, port, encryption, geoInfo,
uuid, clientType, clientName, clientVersion, additionalValues);
if (controlConnection.getLastErrorList() != null && errorSet != null) {
errorSet.addAll(controlConnection.getLastErrorList());
}
if (error != null)
{
System.out.println(error);
return null;
}
//TODO: simple and fast solution; make it better
final String errorNewTest = controlConnection.requestQoSTestParameters(host, pathPrefix, port, encryption, geoInfo,
uuid, clientType, clientName, clientVersion, additionalValues);
if (errorNewTest != null)
{
System.out.println(errorNewTest);
return null;
}
final RMBTTestParameter params = controlConnection.getTestParameter(overrideParams);
return new RMBTClient(params, controlConnection);
}
public static RMBTClient getInstance(final RMBTTestParameter params)
{
return new RMBTClient(params, null);
}
RMBTClient(final RMBTTestParameter params, final ControlServerConnection controlConnection)
{
this.params = params;
this.controlConnection = controlConnection;
params.check();
if (params.getNumThreads() > 0)
{
testThreadPool = Executors.newFixedThreadPool(params.getNumThreads());
testTasks = new RMBTTest[params.getNumThreads()];
}
else
{
testThreadPool = null;
testTasks = null;
}
durationDownNano = params.getDuration() * 1000000000L;
durationUpNano = params.getDuration() * 1000000000L;
lastTransfer = new long[params.getNumThreads()][KEEP_LAST_ENTRIES];
lastTime = new long[params.getNumThreads()][KEEP_LAST_ENTRIES];
if (controlConnection != null)
this.taskDescList = controlConnection.v2TaskDesc;
//if (params.isEncryption())
// sslSocketFactory = createSSLSocketFactory();
}
public void setTrafficService(TrafficService trafficService) {
this.trafficService = trafficService;
}
public TrafficService getTrafficService() {
return this.trafficService;
}
private SSLSocketFactory createSSLSocketFactory()
{
log("initSSL...");
try
{
final SSLContext sc = getSSLContext(null, null);
final SSLSocketFactory factory = sc.getSocketFactory();
return factory;
}
catch (final Exception e)
{
setErrorStatus();
log(e);
}
return null;
}
public static TrustManager getTrustingManager()
{
return new javax.net.ssl.X509TrustManager()
{
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[] {};
}
public void checkClientTrusted(final X509Certificate[] certs, final String authType)
throws CertificateException
{
// System.out.println("[TRUSTING] checkClientTrusted: " +
// Arrays.toString(certs) + " - " + authType);
}
public void checkServerTrusted(final X509Certificate[] certs, final String authType)
throws CertificateException
{
// System.out.println("[TRUSTING] checkServerTrusted: " +
// Arrays.toString(certs) + " - " + authType);
}
};
}
public static SSLContext getSSLContext(final String caResource, final String certResource)
throws NoSuchAlgorithmException, KeyManagementException
{
X509Certificate _ca = null;
try
{
if (caResource != null)
{
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
_ca = (X509Certificate) cf.generateCertificate(RMBTClient.class.getClassLoader().getResourceAsStream(
caResource));
}
}
catch (final Exception e)
{
e.printStackTrace();
}
final X509Certificate ca = _ca;
X509Certificate _cert = null;
try
{
if (certResource != null)
{
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
_cert = (X509Certificate) cf.generateCertificate(RMBTClient.class.getClassLoader().getResourceAsStream(
certResource));
}
}
catch (final Exception e)
{
e.printStackTrace();
}
final X509Certificate cert = _cert;
// TrustManagerFactory tmf = null;
// try
// {
// if (cert != null)
// {
// final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// ks.load(null, null);
// ks.setCertificateEntry("crt", cert);
//
// tmf =
// TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// tmf.init(ks);
// }
// }
// catch (Exception e)
// {
// e.printStackTrace();
// }
final TrustManager tm;
if (cert == null)
tm = getTrustingManager();
else
tm = new javax.net.ssl.X509TrustManager()
{
public X509Certificate[] getAcceptedIssuers()
{
// System.out.println("getAcceptedIssuers");
if (ca == null)
return new X509Certificate[] { cert };
else
return new X509Certificate[] { ca };
}
public void checkClientTrusted(final X509Certificate[] certs, final String authType)
throws CertificateException
{
// System.out.println("checkClientTrusted: " +
// Arrays.toString(certs) + " - " + authType);
}
public void checkServerTrusted(final X509Certificate[] certs, final String authType)
throws CertificateException
{
// System.out.println("checkServerTrusted: " +
// Arrays.toString(certs) + " - " + authType);
if (certs == null)
throw new CertificateException();
for (final X509Certificate c : certs)
if (cert.equals(c))
return;
throw new CertificateException();
}
};
final TrustManager[] trustManagers = new TrustManager[] { tm };
javax.net.ssl.SSLContext sc;
sc = javax.net.ssl.SSLContext.getInstance(Config.RMBT_ENCRYPTION_STRING);
sc.init(null, trustManagers, new java.security.SecureRandom());
return sc;
}
public TestResult runTest() throws InterruptedException
{
System.out.println("starting test...");
long txBytes = 0;
long rxBytes = 0;
final long timeStampStart = System.nanoTime();
if (testStatus.get() != TestStatus.ERROR && testThreadPool != null)
{
if (trafficService != null) {
txBytes = trafficService.getTotalTxBytes();
rxBytes = trafficService.getTotalRxBytes();
}
resetSpeed();
downBitPerSec.set(-1);
upBitPerSec.set(-1);
pingNano.set(-1);
final long waitTime = params.getStartTime() - System.currentTimeMillis();
if (waitTime > 0)
{
setStatus(TestStatus.WAIT);
log(String.format(Locale.US, "we have to wait %d ms...", waitTime));
Thread.sleep(waitTime);
log(String.format(Locale.US, "...done.", waitTime));
}
else
log(String.format(Locale.US, "luckily we do not have to wait.", waitTime));
setStatus(TestStatus.INIT);
statusBeforeError.set(null);
if (testThreadPool.isShutdown())
throw new IllegalStateException("RMBTClient already shut down");
log("starting test...");
final int numThreads = params.getNumThreads();
aborted.set(false);
result = new TotalTestResult();
if (params.isEncryption())
sslSocketFactory = createSSLSocketFactory();
log(String.format(Locale.US, "Host: %s; Port: %s; Enc: %s", params.getHost(), params.getPort(), params.isEncryption()));
log(String.format(Locale.US, "starting %d threads...", numThreads));
final CyclicBarrier barrier = new CyclicBarrier(numThreads);
@SuppressWarnings("unchecked")
final Future<ThreadTestResult>[] results = new Future[numThreads];
final int storeResults = (int) (params.getDuration() * 1000000000L / MIN_DIFF_TIME);
final AtomicBoolean fallbackToOneThread = new AtomicBoolean();
for (int i = 0; i < numThreads; i++)
{
testTasks[i] = new RMBTTest(this, params, i, barrier, storeResults, MIN_DIFF_TIME, fallbackToOneThread);
results[i] = testThreadPool.submit(testTasks[i]);
}
try
{
long shortestPing = Long.MAX_VALUE;
// wait for all threads first
for (int i = 0; i < numThreads; i++)
results[i].get();
if (aborted.get())
return null;
final long[][] allDownBytes = new long[numThreads][];
final long[][] allDownNsecs = new long[numThreads][];
final long[][] allUpBytes = new long[numThreads][];
final long[][] allUpNsecs = new long[numThreads][];
int realNumThreads = 0;
log("");
for (int i = 0; i < numThreads; i++)
{
final ThreadTestResult testResult = results[i].get();
if (testResult != null)
{
realNumThreads++;
log(String.format(Locale.US, "Thread %d: Download: bytes: %d time: %.3f s", i,
ThreadTestResult.getLastEntry(testResult.down.bytes),
ThreadTestResult.getLastEntry(testResult.down.nsec) / 1e9));
log(String.format(Locale.US, "Thread %d: Upload: bytes: %d time: %.3f s", i,
ThreadTestResult.getLastEntry(testResult.up.bytes),
ThreadTestResult.getLastEntry(testResult.up.nsec) / 1e9));
final long ping = testResult.ping_shortest;
if (ping < shortestPing)
shortestPing = ping;
if (!testResult.pings.isEmpty())
result.pings.addAll(testResult.pings);
allDownBytes[i] = testResult.down.bytes;
allDownNsecs[i] = testResult.down.nsec;
allUpBytes[i] = testResult.up.bytes;
allUpNsecs[i] = testResult.up.nsec;
result.totalDownBytes += testResult.totalDownBytes;
result.totalUpBytes += testResult.totalUpBytes;
// aggregate speedItems
result.speedItems.addAll(testResult.speedItems);
}
}
result.calculateDownload(allDownBytes, allDownNsecs);
result.calculateUpload(allUpBytes, allUpNsecs);
log("");
log(String.format(Locale.US, "Total calculated bytes down: %d", result.bytes_download));
log(String.format(Locale.US, "Total calculated time down: %.3f s", result.nsec_download / 1e9));
log(String.format(Locale.US, "Total calculated bytes up: %d", result.bytes_upload));
log(String.format(Locale.US, "Total calculated time up: %.3f s", result.nsec_upload / 1e9));
// get Connection Info from thread 1 (one thread must run)
result.ip_local = results[0].get().ip_local;
result.ip_server = results[0].get().ip_server;
result.port_remote = results[0].get().port_remote;
result.encryption = results[0].get().encryption;
result.num_threads = realNumThreads;
result.ping_shortest = shortestPing;
result.speed_download = result.getDownloadSpeedBitPerSec() / 1e3;
result.speed_upload = result.getUploadSpeedBitPerSec() / 1e3;
log("");
log(String.format(Locale.US, "Total Down: %.0f kBit/s", result.getDownloadSpeedBitPerSec() / 1e3));
log(String.format(Locale.US, "Total UP: %.0f kBit/s", result.getUploadSpeedBitPerSec() / 1e3));
log(String.format(Locale.US, "Ping: %.2f ms", shortestPing / 1e6));
if (controlConnection != null)
{
log("");
final String testId = controlConnection.getTestId();
final String testUUID = params.getUUID();
final long testTime = controlConnection.getTestTime();
log(String.format(Locale.US, "uid=%s, time=%s, uuid=%s\n", testId, new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss.SSS", Locale.US).format(new Date(testTime)), testUUID));
}
downBitPerSec.set(Math.round(result.getDownloadSpeedBitPerSec()));
upBitPerSec.set(Math.round(result.getUploadSpeedBitPerSec()));
log("end.");
setStatus(TestStatus.SPEEDTEST_END);
if (trafficService != null) {
txBytes = trafficService.getTotalTxBytes() - txBytes;
rxBytes = trafficService.getTotalRxBytes() - rxBytes;
result.setTotalTrafficMeasurement(new TestMeasurement(rxBytes, txBytes, timeStampStart, System.nanoTime()));
result.setMeasurementMap(measurementMap);
}
return result;
}
catch (final ExecutionException e)
{
log(e);
abortTest(true);
return null;
}
catch (final InterruptedException e)
{
log("RMBTClient interrupted!");
abortTest(false);
throw e;
}
}
else {
setStatus(TestStatus.SPEEDTEST_END);
return null;
}
}
public boolean abortTest(final boolean error)
{
System.out.println("RMBTClient stopTest");
if (error)
setErrorStatus();
else
setStatus(TestStatus.ABORTED);
aborted.set(true);
if (testThreadPool != null)
testThreadPool.shutdownNow();
return true;
}
public void shutdown()
{
System.out.println("Shutting down RMBT thread pool...");
if (testThreadPool != null)
testThreadPool.shutdownNow();
System.out.println("Shutdown finished.");
}
@Override
protected void finalize() throws Throwable
{
super.finalize();
if (testThreadPool != null)
testThreadPool.shutdownNow();
}
public SSLSocketFactory getSslSocketFactory()
{
return sslSocketFactory;
}
public void setOutputCallback(final RMBTOutputCallback outputCallback)
{
this.outputCallback = outputCallback;
}
private void resetSpeed()
{
lastCounter = 0;
}
private float getTotalSpeed()
{
long sumTrans = 0;
long maxTime = 0;
final CurrentSpeed currentSpeed = new CurrentSpeed();
for (int i = 0; i < params.getNumThreads(); i++)
if (testTasks[i] != null)
{
testTasks[i].getCurrentSpeed(currentSpeed);
if (currentSpeed.time > maxTime)
maxTime = currentSpeed.time;
sumTrans += currentSpeed.trans;
}
return maxTime == 0f ? 0f : (float) sumTrans / (float) maxTime * 1e9f * 8.0f;
}
final Map<Integer, List<SpeedItem>> speedMap = new HashMap<Integer, List<SpeedItem>>();
private float getAvgSpeed()
{
long sumDiffTrans = 0;
long maxDiffTime = 0;
final CurrentSpeed currentSpeed = new CurrentSpeed();
final int currentIndex = lastCounter % KEEP_LAST_ENTRIES;
int diffReferenceIndex = (lastCounter - KEEP_LAST_ENTRIES + 1) % KEEP_LAST_ENTRIES;
if (diffReferenceIndex < 0)
diffReferenceIndex = 0;
lastCounter++;
for (int i = 0; i < params.getNumThreads(); i++)
if (testTasks[i] != null)
{
testTasks[i].getCurrentSpeed(currentSpeed);
lastTime[i][currentIndex] = currentSpeed.time;
lastTransfer[i][currentIndex] = currentSpeed.trans;
// System.out.println("T" + i + ": " + currentSpeed);
List<SpeedItem> speedList = speedMap.get(i);
if (speedList == null) {
speedList = new ArrayList<SpeedItem>();
speedMap.put(i, speedList);
}
speedList.add(new SpeedItem(false, i, currentSpeed.time, currentSpeed.trans));
final long diffTime = currentSpeed.time - lastTime[i][diffReferenceIndex];
final long diffTrans = currentSpeed.trans - lastTransfer[i][diffReferenceIndex];
if (diffTime > maxDiffTime)
maxDiffTime = diffTime;
sumDiffTrans += diffTrans;
}
//TotalTestResult totalResult = TotalTestResult.calculateAndGet(lastTransfer, lastTime, false);
//TotalTestResult totalResult = TotalTestResult.calculateAndGet(speedMap);
final float speedAvg = maxDiffTime == 0f ? 0f : (float) sumDiffTrans / (float) maxDiffTime * 1e9f * 8.0f;
//final float speedAvg = (float)totalResult.speed_download * 1e3f;
// System.out.println("calculate: bytes=" + totalResult.bytes_download + " speed=" + (totalResult.speed_download * 1e3)
// + " nsec=" + totalResult.nsec_download + ", simple: diff=" + sumDiffTrans + " avg=" + speedAvg);
return speedAvg;
}
public IntermediateResult getIntermediateResult(IntermediateResult iResult)
{
if (iResult == null)
iResult = new IntermediateResult();
iResult.status = testStatus.get();
iResult.remainingWait = 0;
final long diffTime = System.nanoTime() - statusChangeTime.get();
switch (iResult.status)
{
case WAIT:
iResult.progress = 0;
iResult.remainingWait = params.getStartTime() - System.currentTimeMillis();
break;
case INIT:
iResult.progress = (float) diffTime / durationInitNano;
break;
case PING:
iResult.progress = getPingProgress();
break;
case DOWN:
iResult.progress = (float) diffTime / durationDownNano;
downBitPerSec.set(Math.round(getAvgSpeed()));
break;
case INIT_UP:
iResult.progress = 0;
break;
case UP:
iResult.progress = (float) diffTime / durationUpNano;
upBitPerSec.set(Math.round(getAvgSpeed()));
break;
case SPEEDTEST_END:
iResult.progress = 1;
break;
case ERROR:
case ABORTED:
iResult.progress = 0;
break;
}
if (iResult.progress > 1)
iResult.progress = 1;
iResult.pingNano = pingNano.get();
iResult.downBitPerSec = downBitPerSec.get();
iResult.upBitPerSec = upBitPerSec.get();
iResult.setLogValues();
return iResult;
}
public TestStatus getStatus()
{
return testStatus.get();
}
public TestStatus getStatusBeforeError()
{
return statusBeforeError.get();
}
public void setStatus(final TestStatus status)
{
testStatus.set(status);
statusChangeTime.set(System.nanoTime());
if (status == TestStatus.INIT_UP)
{
// DOWN is finished
downBitPerSec.set(Math.round(getTotalSpeed()));
resetSpeed();
}
}
public void startTrafficService(final int threadId, final TestStatus status) {
if (trafficService != null) {
//a concurrent map is needed in case multiple threads want to start the traffic service
//only the first thread should be able to start the service
TestMeasurement tm = new TestMeasurement(status.toString(), trafficService);
TestMeasurement previousTm = measurementMap.putIfAbsent(status, tm);
if (previousTm == null) {
tm.start(threadId);
}
}
}
public void stopTrafficMeasurement(final int threadId, final TestStatus status) {
final TestMeasurement testMeasurement = measurementMap.get(status);
if (testMeasurement != null)
testMeasurement.stop(threadId);
}
public Map<TestStatus, TestMeasurement> getTrafficMeasurementMap() {
return measurementMap;
}
public String getErrorMsg()
{
return errorMsg;
}
public void sendResult(final JSONObject additionalValues)
{
if (controlConnection != null) {
final String errorMsg = controlConnection.sendTestResult(result, additionalValues);
if (errorMsg != null)
{
setErrorStatus();
log("Error sending Result...");
log(errorMsg);
}
}
}
public void sendQoSResult(final QoSResultCollector qosResult) {
if (controlConnection != null) {
final String errorMsg = controlConnection.sendQoSResult(result, qosResult.toJson());
if (errorMsg != null)
{
setErrorStatus();
log("Error sending QoS Result...");
log(errorMsg);
}
}
}
private void setErrorStatus()
{
final TestStatus lastStatus = testStatus.getAndSet(TestStatus.ERROR);
if (lastStatus != TestStatus.ERROR)
statusBeforeError.set(lastStatus);
}
void log(final CharSequence text)
{
if (outputToStdout)
System.out.println(text);
if (outputCallback != null)
outputCallback.log(text);
}
void log(final Exception e)
{
if (outputToStdout)
e.printStackTrace(System.out);
if (outputCallback != null)
outputCallback.log(String.format(Locale.US, "Error: %s", e.getMessage()));
}
void setPing(final long shortestPing)
{
pingNano.set(shortestPing);
}
void updatePingStatus(final long tsStart, int pingsDone, long tsLastPing)
{
pingTsStart.set(tsStart);
pingNumDome.set(pingsDone);
pingTsLastPing.set(tsLastPing);
}
private float getPingProgress()
{
final long start = pingTsStart.get();
if (start == -1) // not yet started
return 0;
final int numDone = pingNumDome.get();
final long lastPing = pingTsLastPing.get();
final long now = System.nanoTime();
final int numPings = params.getNumPings();
if (numPings <= 0) // nothing to do
return 1;
final float factorPerPing = (float)1 / (float)numPings;
final float base = factorPerPing * numDone;
final long approxTimePerPing;
if (numDone == 0 || lastPing == -1) // during first ping, assume 100ms
approxTimePerPing = 100000000;
else
approxTimePerPing = (lastPing - start) / numDone;
float factorLastPing = (float)(now - lastPing) / (float)approxTimePerPing;
if (factorLastPing < 0)
factorLastPing = 0;
if (factorLastPing > 1)
factorLastPing = 1;
final float result = base + factorLastPing * factorPerPing;
if (result < 0)
return 0;
if (result > 1)
return 1;
// System.out.println("atpp: " + approxTimePerPing + "; flp:" + factorLastPing+ "; res:" +result);
return result;
}
public String getPublicIP()
{
if (controlConnection == null)
return null;
return controlConnection.getRemoteIp();
}
public String getServerName()
{
if (controlConnection == null)
return null;
return controlConnection.getServerName();
}
public String getProvider()
{
if (controlConnection == null)
return null;
return controlConnection.getProvider();
}
public String getTestUuid()
{
if (controlConnection == null)
return null;
return controlConnection.getTestUuid();
}
public long getStartTimeMillis() {
return controlConnection != null ? controlConnection.getStartTimeMillis() : 0;
}
public ControlServerConnection getControlConnection()
{
return controlConnection;
}
/**
*
* @return
*/
public List<TaskDesc> getTaskDescList() {
return taskDescList;
}
}