diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java index de60a57..e2f6c9d 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java @@ -58,6 +58,7 @@ import cy.agorise.graphenej.network.NodeProvider; import cy.agorise.graphenej.operations.CustomOperation; import cy.agorise.graphenej.operations.LimitOrderCreateOperation; import cy.agorise.graphenej.operations.TransferOperation; +import cy.agorise.graphenej.stats.ExponentialMovingAverage; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.annotations.Nullable; @@ -79,29 +80,69 @@ public class NetworkService extends Service { public static final int NORMAL_CLOSURE_STATUS = 1000; // Time to wait before retrying a connection attempt - private final int DEFAULT_RETRY_DELAY = 5000; + private final int DEFAULT_RETRY_DELAY = 500; + /** + * Constant to be used as a key in order to pass the user name information, in case the + * provided API nodes might require this information. + */ public static final String KEY_USERNAME = "key_username"; + /** + * Constant to be used as a key in order to pass the password information, in case the + * provided API nodes might require this information. + *

+ * This information should be passed as an intent extra when calling the bindService + * or startService methods. + */ public static final String KEY_PASSWORD = "key_password"; + /** + * Constant used as a key in order to specify which APIs the application will be requiring. + *

+ * This information should be passed as an intent extra when calling the bindService + * or startService methods. + */ public static final String KEY_REQUESTED_APIS = "key_requested_apis"; + /** + * Constant used as a key in order to let the NetworkService know whether or not it should + * start a recurring node latency verification task. + *

+ * This information should be passed as an intent extra when calling the bindService + * or startService methods. + */ public static final String KEY_ENABLE_LATENCY_VERIFIER = "key_enable_latency_verifier"; + /** + * Constant used as a key in order to specify the alpha (or smoothing) factor to be used in + * the exponential moving average calculated from the different latency samples. This only + * makes sense if the latency verification feature is enabled of course. + *

+ * This information should be passed as an intent extra when calling the bindService + * or startService methods. + */ + public static final String KEY_NODE_LATENCY_SMOOTHING_FACTOR = "key_node_latency_smoothing_factor"; + /** * Key used to pass via intent a boolean extra to specify whether the connection should * be automatically established. + *

+ * This information should be passed as an intent extra when calling the bindService + * or startService methods. */ public static final String KEY_AUTO_CONNECT = "key_auto_connect"; /** * Key used to pass via intent a list of node URLs. The value passed should be a String * containing a simple comma separated list of URLs. - * + *

* For example: * * wss://domain1.com/ws,wss://domain2.com/ws,wss://domain3.com/ws + *

+ * This information should be passed as an intent extra when calling the bindService + * or startService methods. */ public static final String KEY_NODE_URLS = "key_node_urls"; @@ -264,9 +305,10 @@ public class NetworkService extends Service { // a first round of measurements in order to be sure to select the // best node. if(verifyNodeLatency){ + double alpha = extras.getDouble(KEY_NODE_LATENCY_SMOOTHING_FACTOR, ExponentialMovingAverage.DEFAULT_ALPHA); ArrayList fullNodes = new ArrayList<>(); for(String url : urls){ - fullNodes.add(new FullNode(url)); + fullNodes.add(new FullNode(url, alpha)); } nodeLatencyVerifier = new NodeLatencyVerifier(fullNodes); fullNodePublishSubject = nodeLatencyVerifier.start(); diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java index 669b980..3d31f9b 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkServiceManager.java @@ -15,6 +15,8 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import cy.agorise.graphenej.stats.ExponentialMovingAverage; + /** * This class should be instantiated at the application level of the android app. * @@ -52,6 +54,7 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb private List mCustomNodeUrls = new ArrayList<>(); private boolean mAutoConnect; private boolean mVerifyLatency; + private double alpha; /** * Runnable used to schedule a service disconnection once the app is not visible to the user for @@ -194,6 +197,7 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb private List customNodeUrls; private boolean autoconnect = true; private boolean verifyNodeLatency; + private double alpha = ExponentialMovingAverage.DEFAULT_ALPHA; /** * Sets the user name, if required to connect to a node. @@ -269,6 +273,17 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb return this; } + /** + * Sets the node latency verification's exponential moving average alpha parameter. + * @param alpha The alpha parameter to use when computing the exponential moving average of the + * measured latencies. + * @return The Builder instance. + */ + public Builder setLatencyAverageAlpha(double alpha){ + this.alpha = alpha; + return this; + } + /** * Method used to build a {@link NetworkServiceManager} instance with all of the characteristics * passed as parameters. diff --git a/graphenej/src/main/java/cy/agorise/graphenej/network/FullNode.java b/graphenej/src/main/java/cy/agorise/graphenej/network/FullNode.java index 5da98e0..e98b9f1 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/network/FullNode.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/network/FullNode.java @@ -8,14 +8,35 @@ import cy.agorise.graphenej.stats.ExponentialMovingAverage; public class FullNode implements Comparable { private String mUrl; - private ExponentialMovingAverage latency; + private ExponentialMovingAverage mLatency; private boolean isConnected; private FullNode(){} + /** + * Constructor used to specify both the node URL and the alpha parameter that one wishes to set the + * exponential moving average with. + *

+ * The alpha parameter represents the degree of weighting decrease, and can be specified as any value + * between 0 and 1. A higher alpha discounts older observations faster. + * + * @param url The node URL. + * @param alpha The alpha parameter used to compute the exponential moving average. + */ + public FullNode(String url, double alpha){ + mLatency = new ExponentialMovingAverage(alpha); + mUrl = url; + } + + /** + * Constructor used to specify only the node URL. + *

+ * The alpha parameter is set to the value specified at {@link ExponentialMovingAverage#DEFAULT_ALPHA} + * + * @param url The node URL. + */ public FullNode(String url){ - latency = new ExponentialMovingAverage(ExponentialMovingAverage.DEFAULT_ALPHA); - this.mUrl = url; + this(url, ExponentialMovingAverage.DEFAULT_ALPHA); } /** @@ -39,7 +60,7 @@ public class FullNode implements Comparable { * @return The exponential moving average object instance */ public ExponentialMovingAverage getLatencyAverage(){ - return latency; + return mLatency; } /** @@ -47,7 +68,7 @@ public class FullNode implements Comparable { * @return The latest latency average value */ public double getLatencyValue() { - return latency.getAverage(); + return mLatency.getAverage(); } public boolean isConnected() { @@ -59,17 +80,17 @@ public class FullNode implements Comparable { } /** - * Method that updates the latency average with a new value. - * @param latency Most recent latency sample to be added to the exponential average + * Method that updates the mLatency average with a new value. + * @param latency Most recent mLatency sample to be added to the exponential average */ public void addLatencyValue(double latency) { - this.latency.updateValue(latency); + this.mLatency.updateValue(latency); } @Override public int compareTo(Object o) { FullNode node = (FullNode) o; - return (int) Math.ceil(latency.getAverage() - node.getLatencyValue()); + return (int) Math.ceil(mLatency.getAverage() - node.getLatencyValue()); } @Override diff --git a/graphenej/src/main/java/cy/agorise/graphenej/network/NodeLatencyVerifier.java b/graphenej/src/main/java/cy/agorise/graphenej/network/NodeLatencyVerifier.java index 6e3d29c..a143d3c 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/network/NodeLatencyVerifier.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/network/NodeLatencyVerifier.java @@ -154,7 +154,6 @@ public class NodeLatencyVerifier { long before = timestamps.get(fullNode); delay = after - before; } - fullNode.addLatencyValue(delay); subject.onNext(fullNode); }else{ diff --git a/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java b/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java index f25da84..c911611 100644 --- a/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java +++ b/sample/src/main/java/cy/agorise/labs/sample/SampleApplication.java @@ -38,6 +38,7 @@ public class SampleApplication extends Application { .setCustomNodeUrls(nodes) .setAutoConnect(true) .setNodeLatencyVerification(true) + .setLatencyAverageAlpha(0.1f) .build(this); // Registering this class as a listener to all activity's callback cycle events, in order to