Add counting of different flows to the widget. Change widget styling, add some icons.

This commit is contained in:
Katarzyna Streich 2017-05-19 11:25:56 +01:00
parent 775c26c573
commit 67a417389b
8 changed files with 98 additions and 54 deletions

View File

@ -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
}

View File

@ -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.

View File

@ -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)

View File

@ -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 } }
}
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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()

View File

@ -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;
}