mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-06-01 04:55:00 -04:00
Add KeepAlive handling, add statistics, add table in network view for statistics, reduce connection timeout
This commit is contained in:
parent
54a0fe9cc0
commit
540f49f1e1
31 changed files with 748 additions and 711 deletions
|
@ -196,11 +196,11 @@ public class MainViewModel implements ViewModel {
|
|||
onAllServicesInitialized();
|
||||
});
|
||||
|
||||
startupTimeout = FxTimer.runLater(Duration.ofMillis(60000), () -> {
|
||||
startupTimeout = FxTimer.runLater(Duration.ofMinutes(3), () -> {
|
||||
log.warn("startupTimeout called");
|
||||
MainView.blur();
|
||||
new Popup().warning("The application could not startup after 60 seconds.\n" +
|
||||
"There might be some network connection problems.\n\n" +
|
||||
new Popup().warning("The application could not startup after 3 minutes.\n" +
|
||||
"There might be some network connection problems or a unstable Tor path.\n\n" +
|
||||
"Please restart and try again.")
|
||||
.closeButtonText("Shut down")
|
||||
.onClose(BitsquareApp.shutDownHandler::run)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
<?import io.bitsquare.gui.components.TitledGroupBg?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.cell.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<GridPane fx:id="root" fx:controller="io.bitsquare.gui.main.settings.network.NetworkSettingsView"
|
||||
|
@ -65,12 +66,50 @@
|
|||
</TextField>
|
||||
|
||||
<Label fx:id="p2PPeersLabel" text="Connected peers:" GridPane.rowIndex="4"/>
|
||||
<TextArea fx:id="p2PPeersTextArea" GridPane.rowIndex="4" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
GridPane.vgrow="ALWAYS" editable="false" focusTraversable="false"/>
|
||||
<TableView fx:id="p2PPeerTable" GridPane.rowIndex="4" GridPane.columnIndex="1" VBox.vgrow="ALWAYS"
|
||||
GridPane.hgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Onion address" fx:id="onionAddressColumn" minWidth="220">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="onionAddress"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Type" fx:id="connectionTypeColumn" minWidth="70" maxWidth="80">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="connectionType"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Established" fx:id="creationDateColumn" minWidth="180" maxWidth="180">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="creationDate"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Last activity" fx:id="lastActivityColumn" minWidth="100" maxWidth="120">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="lastActivity"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Sent" fx:id="sentBytesColumn" minWidth="100" maxWidth="120">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="sentBytes"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Received" fx:id="receivedBytesColumn" minWidth="100" maxWidth="120">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="receivedBytes"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Identity" fx:id="peerTypeColumn" minWidth="100" maxWidth="100">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="peerType"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="200.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||
<ColumnConstraints hgrow="NEVER" halignment="RIGHT"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
</GridPane>
|
||||
|
||||
|
|
|
@ -29,8 +29,6 @@ import io.bitsquare.p2p.NodeAddress;
|
|||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.p2p.P2PServiceListener;
|
||||
import io.bitsquare.p2p.network.LocalhostNetworkNode;
|
||||
import io.bitsquare.p2p.network.OutboundConnection;
|
||||
import io.bitsquare.p2p.seed.SeedNodesRepository;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
|
@ -46,7 +44,6 @@ import org.reactfx.util.FxTimer;
|
|||
import javax.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@FxmlView
|
||||
|
@ -63,20 +60,26 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
@FXML
|
||||
ComboBox<BitcoinNetwork> netWorkComboBox;
|
||||
@FXML
|
||||
TextArea bitcoinPeersTextArea, p2PPeersTextArea;
|
||||
TextArea bitcoinPeersTextArea;
|
||||
@FXML
|
||||
Label bitcoinPeersLabel, p2PPeersLabel;
|
||||
@FXML
|
||||
CheckBox useTorCheckBox;
|
||||
|
||||
@FXML
|
||||
TableView<NetworkStatisticListItem> p2PPeerTable;
|
||||
@FXML
|
||||
TableColumn<NetworkStatisticListItem, String> onionAddressColumn, connectionTypeColumn, creationDateColumn,
|
||||
lastActivityColumn, sentBytesColumn, receivedBytesColumn, peerTypeColumn;
|
||||
/* TableColumn<NetworkStatisticListItem, NetworkStatisticListItem> onionAddressColumn, connectionTypeColumn, creationDateColumn,
|
||||
lastActivityColumn, sentBytesColumn, receivedBytesColumn, peerTypeColumn;
|
||||
*/
|
||||
private P2PServiceListener p2PServiceListener;
|
||||
private ChangeListener<Number> numP2PPeersChangeListener;
|
||||
private ChangeListener<List<Peer>> bitcoinPeersChangeListener;
|
||||
private final Set<NodeAddress> seedNodeAddresses;
|
||||
|
||||
@Inject
|
||||
public NetworkSettingsView(WalletService walletService, P2PService p2PService, Preferences preferences,
|
||||
SeedNodesRepository seedNodesRepository, BSFormatter formatter) {
|
||||
BSFormatter formatter) {
|
||||
this.walletService = walletService;
|
||||
this.p2PService = p2PService;
|
||||
this.preferences = preferences;
|
||||
|
@ -84,7 +87,6 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
BitcoinNetwork bitcoinNetwork = preferences.getBitcoinNetwork();
|
||||
|
||||
boolean useLocalhost = p2PService.getNetworkNode() instanceof LocalhostNetworkNode;
|
||||
this.seedNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhost, bitcoinNetwork.ordinal());
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
|
@ -138,6 +140,19 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
public void onSetupFailed(Throwable throwable) {
|
||||
}
|
||||
};
|
||||
|
||||
p2PPeerTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
p2PPeerTable.setPlaceholder(new Label("No connections are available"));
|
||||
p2PPeerTable.getSortOrder().add(creationDateColumn);
|
||||
creationDateColumn.setSortType(TableColumn.SortType.ASCENDING);
|
||||
|
||||
//TODO sorting needs other NetworkStatisticListItem as columns type
|
||||
/* creationDateColumn.setComparator((o1, o2) ->
|
||||
o1.statistic.getCreationDate().compareTo(o2.statistic.getCreationDate()));
|
||||
sentBytesColumn.setComparator((o1, o2) ->
|
||||
((Integer) o1.statistic.getSentBytes()).compareTo(((Integer) o2.statistic.getSentBytes())));
|
||||
receivedBytesColumn.setComparator((o1, o2) ->
|
||||
((Integer) o1.statistic.getReceivedBytes()).compareTo(((Integer) o2.statistic.getReceivedBytes())));*/
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -171,9 +186,9 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
walletService.connectedPeersProperty().addListener(bitcoinPeersChangeListener);
|
||||
updateBitcoinPeersTextArea();
|
||||
|
||||
numP2PPeersChangeListener = (observable, oldValue, newValue) -> updateP2PPeersTextArea();
|
||||
numP2PPeersChangeListener = (observable, oldValue, newValue) -> updateP2PStatistics();
|
||||
p2PService.getNumConnectedPeers().addListener(numP2PPeersChangeListener);
|
||||
updateP2PPeersTextArea();
|
||||
updateP2PStatistics();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -188,24 +203,18 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
|||
|
||||
if (numP2PPeersChangeListener != null)
|
||||
p2PService.getNumConnectedPeers().removeListener(numP2PPeersChangeListener);
|
||||
|
||||
p2PPeerTable.getItems().forEach(NetworkStatisticListItem::cleanup);
|
||||
}
|
||||
|
||||
private void updateP2PPeersTextArea() {
|
||||
p2PPeersTextArea.clear();
|
||||
p2PPeersTextArea.setText(p2PService.getNetworkNode().getConfirmedConnections()
|
||||
.stream()
|
||||
.map(connection -> {
|
||||
if (connection.getPeersNodeAddressOptional().isPresent()) {
|
||||
NodeAddress nodeAddress = connection.getPeersNodeAddressOptional().get();
|
||||
return nodeAddress.getFullAddress() + " (" +
|
||||
(connection instanceof OutboundConnection ? "outbound" : "inbound") +
|
||||
(seedNodeAddresses.contains(nodeAddress) ? " / seed node)" : ")");
|
||||
} else {
|
||||
// Should never be the case
|
||||
return "";
|
||||
}
|
||||
})
|
||||
.collect(Collectors.joining("\n")));
|
||||
private void updateP2PStatistics() {
|
||||
p2PPeerTable.getItems().forEach(NetworkStatisticListItem::cleanup);
|
||||
|
||||
List<NetworkStatisticListItem> list = p2PService.getNetworkNode().getConfirmedConnections().stream()
|
||||
.map(connection -> new NetworkStatisticListItem(connection, formatter))
|
||||
.collect(Collectors.toList());
|
||||
p2PPeerTable.setItems(FXCollections.observableArrayList(list));
|
||||
p2PPeerTable.sort();
|
||||
}
|
||||
|
||||
private void updateBitcoinPeersTextArea() {
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.gui.main.settings.network;
|
||||
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.p2p.network.Connection;
|
||||
import io.bitsquare.p2p.network.OutboundConnection;
|
||||
import io.bitsquare.p2p.network.Statistic;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.reactfx.util.FxTimer;
|
||||
import org.reactfx.util.Timer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class NetworkStatisticListItem {
|
||||
private static final Logger log = LoggerFactory.getLogger(NetworkStatisticListItem.class);
|
||||
|
||||
final Statistic statistic;
|
||||
private final Connection connection;
|
||||
private final Subscription sentBytesSubscription, receivedBytesSubscription;
|
||||
private final Timer timer;
|
||||
private BSFormatter formatter;
|
||||
|
||||
private StringProperty lastActivity = new SimpleStringProperty();
|
||||
private StringProperty sentBytes = new SimpleStringProperty();
|
||||
private StringProperty receivedBytes = new SimpleStringProperty();
|
||||
|
||||
public NetworkStatisticListItem(Connection connection, BSFormatter formatter) {
|
||||
this.connection = connection;
|
||||
this.formatter = formatter;
|
||||
this.statistic = connection.getStatistic();
|
||||
|
||||
sentBytesSubscription = EasyBind.subscribe(statistic.sentBytesProperty,
|
||||
e -> sentBytes.set(formatter.formatBytes((int) e)));
|
||||
receivedBytesSubscription = EasyBind.subscribe(statistic.receivedBytesProperty,
|
||||
e -> receivedBytes.set(formatter.formatBytes((int) e)));
|
||||
|
||||
timer = FxTimer.runPeriodically(Duration.ofMillis(1000),
|
||||
() -> UserThread.execute(() -> onLastActivityChanged(statistic.lastActivityTimestampProperty.get())));
|
||||
onLastActivityChanged(statistic.lastActivityTimestampProperty.get());
|
||||
}
|
||||
|
||||
private void onLastActivityChanged(long timeStamp) {
|
||||
lastActivity.set(DurationFormatUtils.formatDuration(System.currentTimeMillis() - timeStamp, "mm:ss.SSS"));
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
sentBytesSubscription.unsubscribe();
|
||||
receivedBytesSubscription.unsubscribe();
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
public String getOnionAddress() {
|
||||
if (connection.getPeersNodeAddressOptional().isPresent())
|
||||
return connection.getPeersNodeAddressOptional().get().getFullAddress();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getConnectionType() {
|
||||
return connection instanceof OutboundConnection ? "outbound" : "inbound";
|
||||
}
|
||||
|
||||
public String getCreationDate() {
|
||||
return formatter.formatDateTime(statistic.getCreationDate());
|
||||
}
|
||||
|
||||
public String getPeerType() {
|
||||
if (connection.getPeerType() == Connection.PeerType.SEED_NODE)
|
||||
return "Seed node";
|
||||
else if (connection.getPeerType() == Connection.PeerType.DIRECT_MSG_PEER)
|
||||
return "Peer (direct)";
|
||||
else
|
||||
return "Peer";
|
||||
}
|
||||
|
||||
public String getLastActivity() {
|
||||
return lastActivity.get();
|
||||
}
|
||||
|
||||
public StringProperty lastActivityProperty() {
|
||||
return lastActivity;
|
||||
}
|
||||
|
||||
public String getSentBytes() {
|
||||
return sentBytes.get();
|
||||
}
|
||||
|
||||
public StringProperty sentBytesProperty() {
|
||||
return sentBytes;
|
||||
}
|
||||
|
||||
public String getReceivedBytes() {
|
||||
return receivedBytes.get();
|
||||
}
|
||||
|
||||
public StringProperty receivedBytesProperty() {
|
||||
return receivedBytes;
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@ import io.bitsquare.locale.LanguageUtil;
|
|||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
import org.bitcoinj.utils.MonetaryFormat;
|
||||
|
@ -374,8 +375,8 @@ public class BSFormatter {
|
|||
}
|
||||
|
||||
public String getDaysHoursMinutes(Date startDate, Date endDate) {
|
||||
long different = endDate.getTime() - startDate.getTime();
|
||||
long secondsInMilli = 1000;
|
||||
return DurationFormatUtils.formatDurationWords(endDate.getTime() - startDate.getTime(), true, true);
|
||||
/* long secondsInMilli = 1000;
|
||||
long minutesInMilli = secondsInMilli * 60;
|
||||
long hoursInMilli = minutesInMilli * 60;
|
||||
long daysInMilli = hoursInMilli * 24;
|
||||
|
@ -394,9 +395,64 @@ public class BSFormatter {
|
|||
else if (elapsedMinutes > 0)
|
||||
return elapsedMinutes + " " + minuteString;
|
||||
else
|
||||
return null;
|
||||
return null;*/
|
||||
}
|
||||
|
||||
/* public List<String> tokenizePeriod(long time, boolean showSeconds, boolean showMillis) {
|
||||
|
||||
|
||||
log.error("formatPastTimestamp " + time + "");
|
||||
long secondsInMilli = 1000;
|
||||
long minutesInMilli = secondsInMilli * 60;
|
||||
long hoursInMilli = minutesInMilli * 60;
|
||||
long daysInMilli = hoursInMilli * 24;
|
||||
long elapsedDays = time / daysInMilli;
|
||||
time = time % daysInMilli;
|
||||
long elapsedHours = time / hoursInMilli;
|
||||
time = time % hoursInMilli;
|
||||
long elapsedMinutes = time / minutesInMilli;
|
||||
time = time % minutesInMilli;
|
||||
long elapsedSeconds = time / secondsInMilli;
|
||||
time = time % secondsInMilli;
|
||||
long elapsedMillis = time;
|
||||
|
||||
String dayString = elapsedDays == 1 ? elapsedDays + " day" : elapsedDays + " days";
|
||||
String hourString = elapsedHours == 1 ? elapsedHours + " hour" : elapsedHours + " hours";
|
||||
String minuteString = elapsedMinutes == 1 ? elapsedMinutes + " minute" : elapsedMinutes + " minutes";
|
||||
String secondsString = elapsedSeconds == 1 ? elapsedSeconds + " second" : elapsedSeconds + " seconds";
|
||||
String millisString = elapsedMillis + " ms";
|
||||
List<String> tokens = new ArrayList<>();
|
||||
|
||||
if (elapsedDays > 0)
|
||||
tokens.add(dayString);
|
||||
|
||||
if (elapsedHours > 0)
|
||||
tokens.add(hourString);
|
||||
|
||||
if (elapsedMinutes > 0)
|
||||
tokens.add(minuteString);
|
||||
|
||||
if (showSeconds && elapsedSeconds > 0)
|
||||
tokens.add(secondsString);
|
||||
|
||||
if (showMillis && elapsedMillis > 0)
|
||||
tokens.add(millisString);
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private String formatTimeTokens(List<String> tokens) {
|
||||
if (tokens.size() > 1) {
|
||||
String last = tokens.remove(tokens.size() - 1);
|
||||
String result = tokens.stream().collect(Collectors.joining(", "));
|
||||
return result + " and " + last;
|
||||
} else if (tokens.size() == 1) {
|
||||
return tokens.get(0);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
public String booleanToYesNo(boolean value) {
|
||||
return value ? "Yes" : "No";
|
||||
|
@ -429,4 +485,17 @@ public class BSFormatter {
|
|||
else
|
||||
return isOfferer ? "Seller (offerer)" : "Buyer (taker)";
|
||||
}
|
||||
|
||||
public String formatBytes(int bytes) {
|
||||
double kb = 1024;
|
||||
double mb = kb * kb;
|
||||
DecimalFormat decimalFormat = new DecimalFormat("#.##");
|
||||
if (bytes < kb)
|
||||
return bytes + " bytes";
|
||||
else if (bytes < mb)
|
||||
return decimalFormat.format(bytes / kb) + " KB";
|
||||
else
|
||||
return decimalFormat.format(bytes / mb) + " MB";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue