mirror of
https://github.com/corda/corda.git
synced 2025-01-15 17:30:02 +00:00
Add counting of different flows to the widget. Change widget styling, add some icons.
This commit is contained in:
parent
775c26c573
commit
67a417389b
@ -1,11 +1,13 @@
|
||||
package net.corda.client.jfx.model
|
||||
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.collections.FXCollections
|
||||
import net.corda.client.jfx.utils.LeftOuterJoinedMap
|
||||
import net.corda.client.jfx.utils.fold
|
||||
import net.corda.client.jfx.utils.getObservableValues
|
||||
import net.corda.client.jfx.utils.lift
|
||||
import net.corda.client.jfx.utils.recordAsAssociation
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
@ -36,18 +38,35 @@ data class StateMachineData(
|
||||
val id: StateMachineRunId,
|
||||
val stateMachineName: String,
|
||||
val flowInitiator: FlowInitiator,
|
||||
val addRmStatus: ObservableValue<StateMachineStatus>,
|
||||
val stateMachineStatus: ObservableValue<ProgressStatus>
|
||||
val smmStatus: Pair<ObservableValue<StateMachineStatus>, ObservableValue<ProgressStatus>>
|
||||
)
|
||||
|
||||
data class Counter(
|
||||
var errored: SimpleIntegerProperty = SimpleIntegerProperty(0),
|
||||
var success: SimpleIntegerProperty = SimpleIntegerProperty(0),
|
||||
var progress: SimpleIntegerProperty = SimpleIntegerProperty(0)
|
||||
) {
|
||||
fun addSmm() { progress.value += 1 }
|
||||
fun removeSmm(result: ErrorOr<*>) {
|
||||
progress.value -= 1
|
||||
when (result.error) {
|
||||
null -> success.value += 1
|
||||
else -> errored.value += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StateMachineDataModel {
|
||||
private val stateMachineUpdates by observable(NodeMonitorModel::stateMachineUpdates)
|
||||
private val progressTracking by observable(NodeMonitorModel::progressTracking)
|
||||
private val progressEvents = progressTracking.recordAsAssociation(ProgressTrackingEvent::stateMachineId)
|
||||
|
||||
val counter = Counter()
|
||||
|
||||
private val stateMachineStatus = stateMachineUpdates.fold(FXCollections.observableHashMap<StateMachineRunId, SimpleObjectProperty<StateMachineStatus>>()) { map, update ->
|
||||
when (update) {
|
||||
is StateMachineUpdate.Added -> {
|
||||
counter.addSmm()
|
||||
val flowInitiator= update.stateMachineInfo.initiator
|
||||
val added: SimpleObjectProperty<StateMachineStatus> =
|
||||
SimpleObjectProperty(StateMachineStatus.Added(update.stateMachineInfo.flowLogicClassName, flowInitiator))
|
||||
@ -56,6 +75,7 @@ class StateMachineDataModel {
|
||||
is StateMachineUpdate.Removed -> {
|
||||
val added = map[update.id]
|
||||
added ?: throw Exception("State machine removed with unknown id ${update.id}")
|
||||
counter.removeSmm(update.result)
|
||||
added.set(StateMachineStatus.Removed(update.result))
|
||||
}
|
||||
}
|
||||
@ -63,9 +83,12 @@ class StateMachineDataModel {
|
||||
|
||||
private val stateMachineDataList = LeftOuterJoinedMap(stateMachineStatus, progressEvents) { id, status, progress ->
|
||||
val smStatus = status.value as StateMachineStatus.Added
|
||||
StateMachineData(id, smStatus.stateMachineName, smStatus.flowInitiator, status,
|
||||
EasyBind.map(progress) { ProgressStatus(it?.message) })
|
||||
StateMachineData(id, smStatus.stateMachineName, smStatus.flowInitiator,
|
||||
Pair(status, EasyBind.map(progress) { ProgressStatus(it?.message) }))
|
||||
}.getObservableValues()
|
||||
|
||||
val stateMachinesAll = stateMachineDataList
|
||||
val error = counter.errored
|
||||
val success = counter.success
|
||||
val progress = counter.progress
|
||||
}
|
||||
|
@ -11,46 +11,16 @@ import jfxtras.resources.JFXtrasFontRoboto
|
||||
import joptsimple.OptionParser
|
||||
import net.corda.client.jfx.model.Models
|
||||
import net.corda.client.jfx.model.observableValue
|
||||
import net.corda.client.mock.EventGenerator
|
||||
import net.corda.client.mock.Generator
|
||||
import net.corda.client.mock.pickOne
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.GBP
|
||||
import net.corda.core.contracts.USD
|
||||
import net.corda.core.failure
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.core.node.services.ServiceType
|
||||
import net.corda.core.serialization.OpaqueBytes
|
||||
import net.corda.core.success
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.ALICE
|
||||
import net.corda.core.utilities.BOB
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.explorer.model.CordaViewModel
|
||||
import net.corda.explorer.model.SettingsModel
|
||||
import net.corda.explorer.views.*
|
||||
import net.corda.explorer.views.cordapps.cash.CashViewer
|
||||
import net.corda.flows.CashExitFlow
|
||||
import net.corda.flows.CashFlowCommand
|
||||
import net.corda.flows.CashIssueFlow
|
||||
import net.corda.flows.CashPaymentFlow
|
||||
import net.corda.flows.IssuerFlow.IssuanceRequester
|
||||
import net.corda.node.driver.PortAllocation
|
||||
import net.corda.node.driver.driver
|
||||
import net.corda.node.services.startFlowPermission
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.nodeapi.User
|
||||
import org.apache.commons.lang.SystemUtils
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.controlsfx.dialog.ExceptionDialog
|
||||
import tornadofx.App
|
||||
import tornadofx.addStageIcon
|
||||
import tornadofx.find
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Main class for Explorer, you will need Tornado FX to run the explorer.
|
||||
|
@ -4,6 +4,7 @@ import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.Label
|
||||
import tornadofx.*
|
||||
|
||||
class CordaViewModel {
|
||||
@ -31,4 +32,4 @@ abstract class CordaView(title: String? = null) : View(title) {
|
||||
}
|
||||
}
|
||||
|
||||
data class CordaWidget(val name: String, val node: Node)
|
||||
data class CordaWidget(val name: String, val node: Node, val icon: FontAwesomeIcon? = null)
|
@ -1,6 +1,7 @@
|
||||
package net.corda.explorer.views
|
||||
|
||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
|
||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.scene.Node
|
||||
@ -46,6 +47,7 @@ class Dashboard : CordaView() {
|
||||
selectedView.value = view
|
||||
}
|
||||
}
|
||||
it.icon?.let { graphic = FontAwesomeIconView(it).apply { glyphSize = 30.0 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,11 @@ import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.geometry.HPos
|
||||
import javafx.geometry.Pos
|
||||
import javafx.geometry.Insets
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.TableView
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.GridPane
|
||||
import javafx.scene.layout.VBox
|
||||
@ -18,7 +19,8 @@ import net.corda.client.jfx.model.StateMachineData
|
||||
import net.corda.client.jfx.model.StateMachineDataModel
|
||||
import net.corda.client.jfx.model.StateMachineStatus
|
||||
import net.corda.client.jfx.model.observableList
|
||||
import net.corda.client.jfx.model.observableListReadOnly
|
||||
import net.corda.client.jfx.model.observableValue
|
||||
import net.corda.client.jfx.model.writableValue
|
||||
import net.corda.client.jfx.utils.map
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.toBase58String
|
||||
@ -26,33 +28,60 @@ import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.explorer.formatters.FlowInitiatorFormatter
|
||||
import net.corda.explorer.formatters.FlowNameFormatter
|
||||
import net.corda.explorer.formatters.PartyNameFormatter
|
||||
import net.corda.explorer.identicon.identicon
|
||||
import net.corda.explorer.identicon.identiconToolTip
|
||||
import net.corda.explorer.model.CordaView
|
||||
import net.corda.explorer.model.CordaViewModel
|
||||
import net.corda.explorer.model.CordaWidget
|
||||
import net.corda.explorer.ui.setCustomCellFactory
|
||||
import tornadofx.*
|
||||
|
||||
|
||||
// TODO Rethink whole idea of showing communication as table, it should be tree view for each StateMachine (with subflows).
|
||||
class StateMachineViewer : CordaView("Flow Triage") {
|
||||
override val root by fxml<BorderPane>()
|
||||
override val icon = FontAwesomeIcon.HEARTBEAT
|
||||
override val widgets = listOf(CordaWidget(title, StateMachineViewer.StateMachineWidget())).observable()
|
||||
override val widgets = listOf(CordaWidget(title, StateMachineWidget(), icon)).observable()
|
||||
private val allViewTable by fxid<TableView<StateMachineData>>()
|
||||
private val matchingFlowsLabel by fxid<Label>()
|
||||
private val selectedView by writableValue(CordaViewModel::selectedView)
|
||||
|
||||
|
||||
private val stateMachinesAll by observableList(StateMachineDataModel::stateMachinesAll)
|
||||
|
||||
private class StateMachineWidget : BorderPane() {
|
||||
private val stateMachinesAll by observableListReadOnly(StateMachineDataModel::stateMachinesAll)
|
||||
inner private class StateMachineWidget : GridPane() {
|
||||
private val error by observableValue(StateMachineDataModel::error)
|
||||
private val success by observableValue(StateMachineDataModel::success)
|
||||
private val progress by observableValue(StateMachineDataModel::progress)
|
||||
|
||||
init {
|
||||
right {
|
||||
label {
|
||||
textProperty().bind(Bindings.size(stateMachinesAll).map(Number::toString))
|
||||
BorderPane.setAlignment(this, Pos.BOTTOM_RIGHT)
|
||||
}
|
||||
padding = Insets(0.0, 5.0, 10.0, 10.0)
|
||||
hgap = 5.0
|
||||
styleClass += "chart-plot-background"
|
||||
row {
|
||||
label { makeIconLabel(this, FontAwesomeIcon.CHECK, "", "-fx-fill: lightslategrey", 30.0) }
|
||||
label { textProperty().bind(success.map(Number::toString)) }
|
||||
}
|
||||
row {
|
||||
label { makeIconLabel(this, FontAwesomeIcon.BOLT, "", "-fx-fill: lightslategrey", 30.0) }
|
||||
label { textProperty().bind(error.map(Number::toString)) }
|
||||
}
|
||||
row {
|
||||
label { makeIconLabel(this, FontAwesomeIcon.ROCKET, "", "-fx-fill: lightslategrey", 30.0) }
|
||||
label { textProperty().bind(progress.map(Number::toString)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun makeIconLabel(labelNode: Label, icon: FontAwesomeIcon, initText: String, customStyle: String? = null, iconSize: Double = 15.0) {
|
||||
labelNode.apply {
|
||||
graphic = FontAwesomeIconView(icon).apply {
|
||||
glyphSize = iconSize
|
||||
textAlignment = TextAlignment.LEFT
|
||||
style = customStyle
|
||||
}
|
||||
text = initText
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,11 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
private val reportingCurrency by observableValue(ReportingCurrencyModel::reportingCurrency)
|
||||
private val myIdentity by observableValue(NetworkIdentityModel::myIdentity)
|
||||
|
||||
override val widgets = listOf(CordaWidget(title, TransactionWidget())).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
|
||||
|
@ -46,7 +46,7 @@ class CashViewer : CordaView("Cash") {
|
||||
override val root: BorderPane by fxml()
|
||||
override val icon: FontAwesomeIcon = FontAwesomeIcon.MONEY
|
||||
// View's widget.
|
||||
override val widgets = listOf(CordaWidget("Treasury", CashWidget())).observable()
|
||||
override val widgets = listOf(CordaWidget("Treasury", CashWidget(), icon)).observable()
|
||||
// Left pane
|
||||
private val leftPane: VBox by fxid()
|
||||
private val splitPane: SplitPane by fxid()
|
||||
|
@ -111,6 +111,7 @@
|
||||
-fx-cursor: hand;
|
||||
-fx-background-color: -color-1;
|
||||
-fx-border-color: transparent;
|
||||
-fx-border-radius: 2;
|
||||
}
|
||||
|
||||
.tile .title .text, .tile:expanded .title .text {
|
||||
@ -123,14 +124,15 @@
|
||||
-fx-background-repeat: no-repeat;
|
||||
-fx-background-position: center center;
|
||||
-fx-cursor: hand;
|
||||
-fx-padding: 0px;
|
||||
-fx-padding: 5px;
|
||||
-fx-alignment: bottom-right;
|
||||
-fx-border-color: transparent; /*t r b l */
|
||||
-fx-border-radius: 2;
|
||||
}
|
||||
|
||||
.tile .label {
|
||||
-fx-font-size: 2.4em;
|
||||
-fx-padding: 20px;
|
||||
-fx-font-size: 2.0em;
|
||||
-fx-padding: 10px;
|
||||
-fx-text-fill: -color-0;
|
||||
-fx-font-weight: normal;
|
||||
-fx-text-alignment: right;
|
||||
@ -271,9 +273,22 @@
|
||||
-fx-stroke: red;
|
||||
}
|
||||
|
||||
/* Flow details */
|
||||
.smm-detail-grid {
|
||||
-fx-border-color: -color-3;
|
||||
-fx-border-width: 1;
|
||||
/* Other */
|
||||
.identicon {
|
||||
-fx-border-radius: 2;
|
||||
}
|
||||
|
||||
.flow-expanded {
|
||||
-fx-background-color: rgba(255, 255, 255, 0.5);
|
||||
-fx-background-size: Auto 90%;
|
||||
-fx-background-repeat: no-repeat;
|
||||
-fx-background-position: center center;
|
||||
-fx-border-color: transparent;
|
||||
-fx-border-radius: 2;
|
||||
}
|
||||
|
||||
.flow-expanded:hover {
|
||||
-fx-border-color: -color-3;
|
||||
-fx-border-width: 2;
|
||||
-fx-border-radius: 2;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user