mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Move all changes unrelated to flow view in explorer to open source.
This commit is contained in:
parent
b7bec90fae
commit
ab1b7eb551
@ -19,8 +19,7 @@ import rx.subjects.PublishSubject
|
|||||||
data class ProgressTrackingEvent(val stateMachineId: StateMachineRunId, val message: String) {
|
data class ProgressTrackingEvent(val stateMachineId: StateMachineRunId, val message: String) {
|
||||||
companion object {
|
companion object {
|
||||||
fun createStreamFromStateMachineInfo(stateMachine: StateMachineInfo): Observable<ProgressTrackingEvent>? {
|
fun createStreamFromStateMachineInfo(stateMachine: StateMachineInfo): Observable<ProgressTrackingEvent>? {
|
||||||
return stateMachine.progressTrackerStepAndUpdates?.let { pair ->
|
return stateMachine.progressTrackerStepAndUpdates?.let { (current, future) ->
|
||||||
val (current, future) = pair
|
|
||||||
future.map { ProgressTrackingEvent(stateMachine.id, it) }.startWith(ProgressTrackingEvent(stateMachine.id, current))
|
future.map { ProgressTrackingEvent(stateMachine.id, it) }.startWith(ProgressTrackingEvent(stateMachine.id, current))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,6 +74,7 @@ class NodeMonitorModel {
|
|||||||
Observable.empty<ProgressTrackingEvent>()
|
Observable.empty<ProgressTrackingEvent>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to retry, because when flow errors, we unsubscribe from progressTrackingSubject. So we end up with stream of state machine updates and no progress trackers.
|
// We need to retry, because when flow errors, we unsubscribe from progressTrackingSubject. So we end up with stream of state machine updates and no progress trackers.
|
||||||
futureProgressTrackerUpdates.startWith(currentProgressTrackerUpdates).flatMap { it }.retry().subscribe(progressTrackingSubject)
|
futureProgressTrackerUpdates.startWith(currentProgressTrackerUpdates).flatMap { it }.retry().subscribe(progressTrackingSubject)
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import java.util.*
|
|||||||
* Especially at the beginning of simulation there might be few insufficient spend errors.
|
* Especially at the beginning of simulation there might be few insufficient spend errors.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class EventGenerator(val parties: List<Party>, val currencies: List<Currency>, val notary: Party) {
|
open class EventGenerator(val parties: List<Party>, val currencies: List<Currency>, val notary: Party) {
|
||||||
protected val partyGenerator = Generator.pickOne(parties)
|
protected val partyGenerator = Generator.pickOne(parties)
|
||||||
protected val issueRefGenerator = Generator.intRange(0, 1).map { number -> OpaqueBytes(ByteArray(1, { number.toByte() })) }
|
protected val issueRefGenerator = Generator.intRange(0, 1).map { number -> OpaqueBytes(ByteArray(1, { number.toByte() })) }
|
||||||
protected val amountGenerator = Generator.longRange(10000, 1000000)
|
protected val amountGenerator = Generator.longRange(10000, 1000000)
|
||||||
@ -34,12 +34,57 @@ class EventGenerator(val parties: List<Party>, val currencies: List<Currency>, v
|
|||||||
CashFlowCommand.ExitCash(Amount(amount, ccy), issueRef)
|
CashFlowCommand.ExitCash(Amount(amount, ccy), issueRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
val moveCashGenerator = amountGenerator.combine(partyGenerator, currencyGenerator) { amountIssued, recipient, currency ->
|
open val moveCashGenerator = amountGenerator.combine(partyGenerator, currencyGenerator) { amountIssued, recipient, currency ->
|
||||||
CashFlowCommand.PayCash(Amount(amountIssued, currency), recipient)
|
CashFlowCommand.PayCash(Amount(amountIssued, currency), recipient)
|
||||||
}
|
}
|
||||||
|
|
||||||
val issuerGenerator = Generator.frequency(listOf(
|
open val issuerGenerator = Generator.frequency(listOf(
|
||||||
0.1 to exitCashGenerator,
|
0.1 to exitCashGenerator,
|
||||||
0.9 to issueCashGenerator
|
0.9 to issueCashGenerator
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Generator]s for incoming/outgoing events of starting different cash flows. It invokes flows that throw exceptions
|
||||||
|
* for use in explorer flow triage. Exceptions are of kind spending/exiting too much cash.
|
||||||
|
*/
|
||||||
|
class ErrorFlowsEventGenerator(parties: List<Party>, currencies: List<Currency>, notary: Party): EventGenerator(parties, currencies, notary) {
|
||||||
|
enum class IssuerEvents {
|
||||||
|
NORMAL_EXIT,
|
||||||
|
EXIT_ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
val errorGenerator = Generator.pickOne(IssuerEvents.values().toList())
|
||||||
|
|
||||||
|
val errorExitCashGenerator = amountGenerator.combine(issueRefGenerator, currencyGenerator, errorGenerator) { amount, issueRef, ccy, errorType ->
|
||||||
|
when (errorType) {
|
||||||
|
IssuerEvents.NORMAL_EXIT -> {
|
||||||
|
println("Normal exit")
|
||||||
|
if (currencyMap[ccy]!! <= amount) addToMap(ccy, -amount)
|
||||||
|
CashFlowCommand.ExitCash(Amount(amount, ccy), issueRef) // It may fail at the beginning, but we don't care.
|
||||||
|
}
|
||||||
|
IssuerEvents.EXIT_ERROR -> {
|
||||||
|
println("Exit error")
|
||||||
|
CashFlowCommand.ExitCash(Amount(currencyMap[ccy]!! * 2, ccy), issueRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val normalMoveGenerator = amountGenerator.combine(partyGenerator, currencyGenerator) { amountIssued, recipient, currency ->
|
||||||
|
CashFlowCommand.PayCash(Amount(amountIssued, currency), recipient)
|
||||||
|
}
|
||||||
|
|
||||||
|
val errorMoveGenerator = partyGenerator.combine(currencyGenerator) { recipient, currency ->
|
||||||
|
CashFlowCommand.PayCash(Amount(currencyMap[currency]!! * 2, currency), recipient)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val moveCashGenerator = Generator.frequency(listOf(
|
||||||
|
0.2 to errorMoveGenerator,
|
||||||
|
0.8 to normalMoveGenerator
|
||||||
|
))
|
||||||
|
|
||||||
|
override val issuerGenerator = Generator.frequency(listOf(
|
||||||
|
0.3 to errorExitCashGenerator,
|
||||||
|
0.7 to issueCashGenerator
|
||||||
|
))
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.explorer
|
package net.corda.explorer
|
||||||
|
|
||||||
import joptsimple.OptionSet
|
import joptsimple.OptionSet
|
||||||
|
import net.corda.client.mock.ErrorFlowsEventGenerator
|
||||||
import net.corda.client.mock.EventGenerator
|
import net.corda.client.mock.EventGenerator
|
||||||
import net.corda.client.mock.Generator
|
import net.corda.client.mock.Generator
|
||||||
import net.corda.client.mock.pickOne
|
import net.corda.client.mock.pickOne
|
||||||
@ -99,6 +100,7 @@ class ExplorerSimulation(val options: OptionSet) {
|
|||||||
|
|
||||||
when {
|
when {
|
||||||
options.has("S") -> startNormalSimulation()
|
options.has("S") -> startNormalSimulation()
|
||||||
|
options.has("F") -> startErrorFlowsSimulation()
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForAllNodesToFinish()
|
waitForAllNodesToFinish()
|
||||||
@ -188,4 +190,17 @@ class ExplorerSimulation(val options: OptionSet) {
|
|||||||
startSimulation(eventGenerator, maxIterations)
|
startSimulation(eventGenerator, maxIterations)
|
||||||
onEnd()
|
onEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startErrorFlowsSimulation() {
|
||||||
|
println("Running flows with errors simulation mode ...")
|
||||||
|
setUpRPC()
|
||||||
|
val eventGenerator = ErrorFlowsEventGenerator(
|
||||||
|
parties = parties.map { it.first },
|
||||||
|
notary = notaryNode.nodeInfo.notaryIdentity,
|
||||||
|
currencies = listOf(GBP, USD)
|
||||||
|
)
|
||||||
|
val maxIterations = 10_000
|
||||||
|
startSimulation(eventGenerator, maxIterations)
|
||||||
|
onEnd()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,9 @@ import net.corda.explorer.views.*
|
|||||||
import net.corda.explorer.views.cordapps.cash.CashViewer
|
import net.corda.explorer.views.cordapps.cash.CashViewer
|
||||||
import org.apache.commons.lang.SystemUtils
|
import org.apache.commons.lang.SystemUtils
|
||||||
import org.controlsfx.dialog.ExceptionDialog
|
import org.controlsfx.dialog.ExceptionDialog
|
||||||
import tornadofx.*
|
import tornadofx.App
|
||||||
|
import tornadofx.addStageIcon
|
||||||
|
import tornadofx.find
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main class for Explorer, you will need Tornado FX to run the explorer.
|
* Main class for Explorer, you will need Tornado FX to run the explorer.
|
||||||
@ -123,7 +125,7 @@ class Main : App(MainView::class) {
|
|||||||
* On each iteration, the issuers will execute a Cash Issue or Cash Exit flow (at a 9:1 ratio) and a random party will execute a move of cash to another random party.
|
* On each iteration, the issuers will execute a Cash Issue or Cash Exit flow (at a 9:1 ratio) and a random party will execute a move of cash to another random party.
|
||||||
*/
|
*/
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val parser = OptionParser("S")
|
val parser = OptionParser("SF")
|
||||||
val options = parser.parse(*args)
|
val options = parser.parse(*args)
|
||||||
ExplorerSimulation(options)
|
ExplorerSimulation(options)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import javafx.animation.FadeTransition
|
|||||||
import javafx.animation.TranslateTransition
|
import javafx.animation.TranslateTransition
|
||||||
import javafx.beans.binding.Bindings
|
import javafx.beans.binding.Bindings
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
import javafx.beans.value.ObservableValue
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
import javafx.geometry.Bounds
|
import javafx.geometry.Bounds
|
||||||
import javafx.geometry.Point2D
|
import javafx.geometry.Point2D
|
||||||
@ -41,6 +42,9 @@ class Network : CordaView() {
|
|||||||
val notaries by observableList(NetworkIdentityModel::notaries)
|
val notaries by observableList(NetworkIdentityModel::notaries)
|
||||||
val peers by observableList(NetworkIdentityModel::parties)
|
val peers by observableList(NetworkIdentityModel::parties)
|
||||||
val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions)
|
val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions)
|
||||||
|
var centralPeer: String? = null
|
||||||
|
private var centralLabel: ObservableValue<Label?>
|
||||||
|
|
||||||
// UI components
|
// UI components
|
||||||
private val myIdentityPane by fxid<BorderPane>()
|
private val myIdentityPane by fxid<BorderPane>()
|
||||||
private val notaryList by fxid<VBox>()
|
private val notaryList by fxid<VBox>()
|
||||||
@ -61,6 +65,7 @@ class Network : CordaView() {
|
|||||||
private val peerButtons = peerComponents.filtered { it.nodeInfo != myIdentity.value }.map { it.button }
|
private val peerButtons = peerComponents.filtered { it.nodeInfo != myIdentity.value }.map { it.button }
|
||||||
private val allComponents = FXCollections.observableArrayList(notaryComponents, peerComponents).concatenate()
|
private val allComponents = FXCollections.observableArrayList(notaryComponents, peerComponents).concatenate()
|
||||||
private val allComponentMap = allComponents.associateBy { it.nodeInfo.legalIdentity }
|
private val allComponentMap = allComponents.associateBy { it.nodeInfo.legalIdentity }
|
||||||
|
private val mapLabels = allComponents.map { it.label }
|
||||||
|
|
||||||
private data class MapViewComponents(val nodeInfo: NodeInfo, val button: Button, val label: Label)
|
private data class MapViewComponents(val nodeInfo: NodeInfo, val button: Button, val label: Label)
|
||||||
|
|
||||||
@ -133,19 +138,29 @@ class Network : CordaView() {
|
|||||||
return MapViewComponents(this, button, mapLabel)
|
return MapViewComponents(this, button, mapLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDock() {
|
||||||
|
centralLabel = mapLabels.firstOrDefault(SimpleObjectProperty(myLabel), { centralPeer?.contains(it.text, true) ?: false })
|
||||||
|
centralLabel.value?.let { mapScrollPane.centerLabel(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUndock() {
|
||||||
|
centralPeer = null
|
||||||
|
centralLabel = SimpleObjectProperty(myLabel)
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
centralLabel = mapLabels.firstOrDefault(SimpleObjectProperty(myLabel), { centralPeer?.contains(it.text, true) ?: false })
|
||||||
Bindings.bindContent(notaryList.children, notaryButtons)
|
Bindings.bindContent(notaryList.children, notaryButtons)
|
||||||
Bindings.bindContent(peerList.children, peerButtons)
|
Bindings.bindContent(peerList.children, peerButtons)
|
||||||
// Run once when the screen is ready.
|
// Run once when the screen is ready.
|
||||||
// TODO : Find a better way to do this.
|
// TODO : Find a better way to do this.
|
||||||
mapPane.heightProperty().addListener { _, old, _ ->
|
mapPane.heightProperty().addListener { _, old, _ ->
|
||||||
if (old == 0.0) myLabel?.let {
|
if (old == 0.0) centralLabel.value?.let {
|
||||||
mapPane.applyCss()
|
mapPane.applyCss()
|
||||||
mapPane.layout()
|
mapPane.layout()
|
||||||
mapScrollPane.centerLabel(it)
|
mapScrollPane.centerLabel(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen on zooming gesture, if device has gesture support.
|
// Listen on zooming gesture, if device has gesture support.
|
||||||
mapPane.setOnZoom { zoom(it.zoomFactor, Point2D(it.x, it.y)) }
|
mapPane.setOnZoom { zoom(it.zoomFactor, Point2D(it.x, it.y)) }
|
||||||
|
|
||||||
@ -164,6 +179,7 @@ class Network : CordaView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO It doesn't work as expected.
|
||||||
private fun ScrollPane.centerLabel(label: Label) {
|
private fun ScrollPane.centerLabel(label: Label) {
|
||||||
this.hvalue = (label.boundsInParent.width / 2 + label.boundsInParent.minX) / mapImageView.layoutBounds.width
|
this.hvalue = (label.boundsInParent.width / 2 + label.boundsInParent.minX) / mapImageView.layoutBounds.width
|
||||||
this.vvalue = (label.boundsInParent.height / 2 + label.boundsInParent.minY) / mapImageView.layoutBounds.height
|
this.vvalue = (label.boundsInParent.height / 2 + label.boundsInParent.minY) / mapImageView.layoutBounds.height
|
||||||
|
@ -55,6 +55,10 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
|
|
||||||
override val widgets = listOf(CordaWidget(title, TransactionWidget(), icon)).observable()
|
override val widgets = listOf(CordaWidget(title, TransactionWidget(), icon)).observable()
|
||||||
|
|
||||||
|
private var scrollPosition: Int = 0
|
||||||
|
private lateinit var expander: ExpanderColumn<TransactionViewer.Transaction>
|
||||||
|
var txIdToScroll: SecureHash? = null // Passed as param.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is what holds data for a single transaction node. Note how a lot of these are nullable as we often simply don't
|
* This is what holds data for a single transaction node. Note how a lot of these are nullable as we often simply don't
|
||||||
* have the data.
|
* have the data.
|
||||||
@ -72,6 +76,26 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
|
|
||||||
data class Inputs(val resolved: ObservableList<StateAndRef<ContractState>>, val unresolved: ObservableList<StateRef>)
|
data class Inputs(val resolved: ObservableList<StateAndRef<ContractState>>, val unresolved: ObservableList<StateRef>)
|
||||||
|
|
||||||
|
override fun onDock() {
|
||||||
|
txIdToScroll?.let {
|
||||||
|
scrollPosition = transactionViewTable.items.indexOfFirst { it.id == txIdToScroll }
|
||||||
|
if (scrollPosition > 0) {
|
||||||
|
expander.toggleExpanded(scrollPosition)
|
||||||
|
val tx = transactionViewTable.items[scrollPosition]
|
||||||
|
transactionViewTable.scrollTo(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUndock() {
|
||||||
|
if (scrollPosition != 0) {
|
||||||
|
val isExpanded = expander.getExpandedProperty(transactionViewTable.items[scrollPosition])
|
||||||
|
if (isExpanded.value) expander.toggleExpanded(scrollPosition)
|
||||||
|
scrollPosition = 0
|
||||||
|
}
|
||||||
|
txIdToScroll = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We map the gathered data about transactions almost one-to-one to the nodes.
|
* We map the gathered data about transactions almost one-to-one to the nodes.
|
||||||
*/
|
*/
|
||||||
@ -158,7 +182,7 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
titleProperty.bind(reportingCurrency.map { "Total value ($it equiv)" })
|
titleProperty.bind(reportingCurrency.map { "Total value ($it equiv)" })
|
||||||
}
|
}
|
||||||
|
|
||||||
rowExpander {
|
expander = rowExpander {
|
||||||
add(ContractStatesView(it).root)
|
add(ContractStatesView(it).root)
|
||||||
prefHeight = 400.0
|
prefHeight = 400.0
|
||||||
}.apply {
|
}.apply {
|
||||||
|
@ -311,8 +311,8 @@
|
|||||||
.scroll-bar:vertical {
|
.scroll-bar:vertical {
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
}
|
}
|
||||||
+/* Other */
|
|
||||||
|
|
||||||
|
/* Other */
|
||||||
.identicon {
|
.identicon {
|
||||||
-fx-border-radius: 2;
|
-fx-border-radius: 2;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user