Code cleanup

* Removed code scaffolding.
* Removed useless parenthesis.
* Added todo.
* Extracted variable declaration to improve readability.
* Renamed VisualiserModel to VisualiserViewModel.
This commit is contained in:
Clinton Alexander 2016-07-26 11:25:59 +01:00 committed by Ross Nicoll
parent f76c7c9cf9
commit 49beda43f9
3 changed files with 45 additions and 69 deletions

View File

@ -44,16 +44,13 @@ import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
import kotlin.concurrent.scheduleAtFixedRate import kotlin.concurrent.scheduleAtFixedRate
import kotlin.system.exitProcess import kotlin.system.exitProcess
import com.r3cev.corda.netmap.VisualiserModel.Style import com.r3cev.corda.netmap.VisualiserViewModel.Style
fun <T : Any> WritableValue<T>.keyValue(endValue: T, interpolator: Interpolator = Interpolator.EASE_OUT) = KeyValue(this, endValue, interpolator) fun <T : Any> WritableValue<T>.keyValue(endValue: T, interpolator: Interpolator = Interpolator.EASE_OUT) = KeyValue(this, endValue, interpolator)
// TODO: This code is all horribly ugly. Refactor to use TornadoFX to clean it up. // TODO: This code is all horribly ugly. Refactor to use TornadoFX to clean it up.
class NetworkMapVisualiser : Application() { class NetworkMapVisualiser : Application() {
//========================
// Enums
//========================
enum class NodeType { enum class NodeType {
BANK, SERVICE BANK, SERVICE
} }
@ -66,9 +63,6 @@ class NetworkMapVisualiser : Application() {
} }
} }
//========================
// Classes
//========================
sealed class RunningPausedState { sealed class RunningPausedState {
class Running(val tickTimer: TimerTask): RunningPausedState() class Running(val tickTimer: TimerTask): RunningPausedState()
class Paused(): RunningPausedState() class Paused(): RunningPausedState()
@ -82,32 +76,27 @@ class NetworkMapVisualiser : Application() {
} }
} }
//========================
// Object variables
//========================
private val view = VisualiserView() private val view = VisualiserView()
private val model = VisualiserModel() private val viewModel = VisualiserViewModel()
val timer = Timer() val timer = Timer()
val uiThread: Scheduler = Schedulers.from { Platform.runLater(it) } val uiThread: Scheduler = Schedulers.from { Platform.runLater(it) }
//========================
// Init and shutdown
//========================
init { init {
BriefLogFormatter.initVerbose(InMemoryMessagingNetwork.MESSAGES_LOG_NAME) BriefLogFormatter.initVerbose(InMemoryMessagingNetwork.MESSAGES_LOG_NAME)
} }
override fun start(stage: Stage) { override fun start(stage: Stage) {
model.view = view viewModel.view = view
model.presentationMode = "--presentation-mode" in parameters.raw viewModel.presentationMode = "--presentation-mode" in parameters.raw
buildScene(stage) buildScene(stage)
model.displayStyle = if ("--circle" in parameters.raw) { Style.CIRCLE } else { model.displayStyle } viewModel.displayStyle = if ("--circle" in parameters.raw) { Style.CIRCLE } else { viewModel.displayStyle }
val simulation = viewModel.simulation
// Update the white-backgrounded label indicating what protocol step it's up to. // Update the white-backgrounded label indicating what protocol step it's up to.
model.simulation.allProtocolSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> -> simulation.allProtocolSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> ->
val (node, change) = step val (node, change) = step
val label = model.nodesToWidgets[node]!!.statusLabel val label = viewModel.nodesToWidgets[node]!!.statusLabel
if (change is ProgressTracker.Change.Position) { if (change is ProgressTracker.Change.Position) {
// Fade in the status label if it's our first step. // Fade in the status label if it's our first step.
if (label.text == "") { if (label.text == "") {
@ -134,18 +123,18 @@ class NetworkMapVisualiser : Application() {
} }
} }
// Fire the message bullets between nodes. // Fire the message bullets between nodes.
model.simulation.network.messagingNetwork.sentMessages.observeOn(uiThread).subscribe { msg: InMemoryMessagingNetwork.MessageTransfer -> simulation.network.messagingNetwork.sentMessages.observeOn(uiThread).subscribe { msg: InMemoryMessagingNetwork.MessageTransfer ->
val senderNode: MockNetwork.MockNode = model.simulation.network.addressToNode(msg.sender.myAddress) val senderNode: MockNetwork.MockNode = simulation.network.addressToNode(msg.sender.myAddress)
val destNode: MockNetwork.MockNode = model.simulation.network.addressToNode(msg.recipients as SingleMessageRecipient) val destNode: MockNetwork.MockNode = simulation.network.addressToNode(msg.recipients as SingleMessageRecipient)
if (transferIsInteresting(msg)) { if (transferIsInteresting(msg)) {
model.nodesToWidgets[senderNode]!!.pulseAnim.play() viewModel.nodesToWidgets[senderNode]!!.pulseAnim.play()
model.fireBulletBetweenNodes(senderNode, destNode, "bank", "bank") viewModel.fireBulletBetweenNodes(senderNode, destNode, "bank", "bank")
} }
} }
// Pulse all parties in a trade when the trade completes // Pulse all parties in a trade when the trade completes
model.simulation.doneSteps.observeOn(uiThread).subscribe { nodes: Collection<Simulation.SimulatedNode> -> simulation.doneSteps.observeOn(uiThread).subscribe { nodes: Collection<Simulation.SimulatedNode> ->
nodes.forEach { model.nodesToWidgets[it]!!.longPulseAnim.play() } nodes.forEach { viewModel.nodesToWidgets[it]!!.longPulseAnim.play() }
} }
stage.setOnCloseRequest { exitProcess(0) } stage.setOnCloseRequest { exitProcess(0) }
@ -169,15 +158,12 @@ class NetworkMapVisualiser : Application() {
} }
} }
//========================
// View functions
//========================
private fun buildScene(stage: Stage) { private fun buildScene(stage: Stage) {
view.stage = stage view.stage = stage
view.setup(model.runningPausedState, model.displayStyle, model.presentationMode) view.setup(viewModel.runningPausedState, viewModel.displayStyle, viewModel.presentationMode)
bindSidebar() bindSidebar()
bindTopbar() bindTopbar()
model.createNodes() viewModel.createNodes()
// Spacebar advances simulation by one step. // Spacebar advances simulation by one step.
stage.scene.accelerators[KeyCodeCombination(KeyCode.SPACE)] = Runnable { onNextInvoked() } stage.scene.accelerators[KeyCodeCombination(KeyCode.SPACE)] = Runnable { onNextInvoked() }
@ -194,17 +180,17 @@ class NetworkMapVisualiser : Application() {
private fun bindTopbar() { private fun bindTopbar() {
view.resetButton.setOnAction({reset()}) view.resetButton.setOnAction({reset()})
view.nextButton.setOnAction { view.nextButton.setOnAction {
if (!view.simulateInitialisationCheckbox.isSelected && !model.simulation.networkInitialisationFinished.isDone) { if (!view.simulateInitialisationCheckbox.isSelected && !viewModel.simulation.networkInitialisationFinished.isDone) {
skipNetworkInitialisation() skipNetworkInitialisation()
} else { } else {
onNextInvoked() onNextInvoked()
} }
} }
model.simulation.networkInitialisationFinished.then { viewModel.simulation.networkInitialisationFinished.then {
view.simulateInitialisationCheckbox.isVisible = false view.simulateInitialisationCheckbox.isVisible = false
} }
view.runPauseButton.setOnAction { view.runPauseButton.setOnAction {
val oldRunningPausedState = model.runningPausedState val oldRunningPausedState = viewModel.runningPausedState
val newRunningPausedState = when (oldRunningPausedState) { val newRunningPausedState = when (oldRunningPausedState) {
is NetworkMapVisualiser.RunningPausedState.Running -> { is NetworkMapVisualiser.RunningPausedState.Running -> {
oldRunningPausedState.tickTimer.cancel() oldRunningPausedState.tickTimer.cancel()
@ -215,7 +201,7 @@ class NetworkMapVisualiser : Application() {
NetworkMapVisualiser.RunningPausedState.Paused() NetworkMapVisualiser.RunningPausedState.Paused()
} }
is NetworkMapVisualiser.RunningPausedState.Paused -> { is NetworkMapVisualiser.RunningPausedState.Paused -> {
val tickTimer = timer.scheduleAtFixedRate(model.stepDuration.toMillis().toLong(), model.stepDuration.toMillis().toLong()) { val tickTimer = timer.scheduleAtFixedRate(viewModel.stepDuration.toMillis().toLong(), viewModel.stepDuration.toMillis().toLong()) {
Platform.runLater { Platform.runLater {
onNextInvoked() onNextInvoked()
} }
@ -224,7 +210,7 @@ class NetworkMapVisualiser : Application() {
view.nextButton.isDisable = true view.nextButton.isDisable = true
view.resetButton.isDisable = true view.resetButton.isDisable = true
if (!view.simulateInitialisationCheckbox.isSelected && !model.simulation.networkInitialisationFinished.isDone) { if (!view.simulateInitialisationCheckbox.isSelected && !viewModel.simulation.networkInitialisationFinished.isDone) {
skipNetworkInitialisation() skipNetworkInitialisation()
} }
@ -233,16 +219,17 @@ class NetworkMapVisualiser : Application() {
} }
view.runPauseButton.text = newRunningPausedState.buttonLabel.toString() view.runPauseButton.text = newRunningPausedState.buttonLabel.toString()
model.runningPausedState = newRunningPausedState viewModel.runningPausedState = newRunningPausedState
} }
view.styleChoice.selectionModel.selectedItemProperty() view.styleChoice.selectionModel.selectedItemProperty()
.addListener { ov, value, newValue -> model.displayStyle = newValue } .addListener { ov, value, newValue -> viewModel.displayStyle = newValue }
model.simulation.dateChanges.observeOn(uiThread).subscribe { view.dateLabel.text = it.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)) } viewModel.simulation.dateChanges.observeOn(uiThread).subscribe { view.dateLabel.text = it.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)) }
} }
private fun reloadStylesheet(stage: Stage) { private fun reloadStylesheet(stage: Stage) {
stage.scene.stylesheets.clear() stage.scene.stylesheets.clear()
// TODO: Improve path resolution to avoid hardcoding a specific user directory.
// Enable hot reload without needing to rebuild. // Enable hot reload without needing to rebuild.
val mikesCSS = "/Users/mike/Source/R3/r3dlg-prototyping/network-explorer/src/main/resources/com/r3cev/corda/netmap/styles.css" val mikesCSS = "/Users/mike/Source/R3/r3dlg-prototyping/network-explorer/src/main/resources/com/r3cev/corda/netmap/styles.css"
if (Files.exists(Paths.get(mikesCSS))) if (Files.exists(Paths.get(mikesCSS)))
@ -251,11 +238,8 @@ class NetworkMapVisualiser : Application() {
stage.scene.stylesheets.add(NetworkMapVisualiser::class.java.getResource("styles.css").toString()) stage.scene.stylesheets.add(NetworkMapVisualiser::class.java.getResource("styles.css").toString())
} }
//========================
// Temp
//========================
private fun bindSidebar() { private fun bindSidebar() {
model.simulation.allProtocolSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> -> viewModel.simulation.allProtocolSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> ->
val (node, change) = step val (node, change) = step
if (change is ProgressTracker.Change.Position) { if (change is ProgressTracker.Change.Position) {
@ -265,18 +249,18 @@ class NetworkMapVisualiser : Application() {
// Protocol done; schedule it for removal in a few seconds. We batch them up to make nicer // Protocol done; schedule it for removal in a few seconds. We batch them up to make nicer
// animations. // animations.
println("Protocol done for ${node.info.identity.name}") println("Protocol done for ${node.info.identity.name}")
model.doneTrackers += tracker viewModel.doneTrackers += tracker
} else { } else {
// Subprotocol is done; ignore it. // Subprotocol is done; ignore it.
} }
} else if (!model.trackerBoxes.containsKey(tracker)) { } else if (!viewModel.trackerBoxes.containsKey(tracker)) {
// New protocol started up; add. // New protocol started up; add.
val extraLabel = model.simulation.extraNodeLabels[node] val extraLabel = viewModel.simulation.extraNodeLabels[node]
val label = if (extraLabel != null) "${node.storage.myLegalIdentity.name}: $extraLabel" else node.storage.myLegalIdentity.name val label = if (extraLabel != null) "${node.storage.myLegalIdentity.name}: $extraLabel" else node.storage.myLegalIdentity.name
val widget = view.buildProgressTrackerWidget(label, tracker.topLevelTracker) val widget = view.buildProgressTrackerWidget(label, tracker.topLevelTracker)
bindProgressTracketWidget(tracker.topLevelTracker, widget) bindProgressTracketWidget(tracker.topLevelTracker, widget)
println("Added: ${tracker}, ${widget}") println("Added: ${tracker}, ${widget}")
model.trackerBoxes[tracker] = widget.vbox viewModel.trackerBoxes[tracker] = widget.vbox
view.sidebar.children += widget.vbox view.sidebar.children += widget.vbox
} }
} }
@ -284,8 +268,8 @@ class NetworkMapVisualiser : Application() {
Timer().scheduleAtFixedRate(0, 500) { Timer().scheduleAtFixedRate(0, 500) {
Platform.runLater { Platform.runLater {
for (tracker in model.doneTrackers) { for (tracker in viewModel.doneTrackers) {
val pane = model.trackerBoxes[tracker]!! val pane = viewModel.trackerBoxes[tracker]!!
// Slide the other tracker widgets up and over this one. // Slide the other tracker widgets up and over this one.
val slideProp = SimpleDoubleProperty(0.0) val slideProp = SimpleDoubleProperty(0.0)
slideProp.addListener { obv -> pane.padding = Insets(0.0, 0.0, slideProp.value, 0.0) } slideProp.addListener { obv -> pane.padding = Insets(0.0, 0.0, slideProp.value, 0.0) }
@ -297,12 +281,12 @@ class NetworkMapVisualiser : Application() {
) )
timeline.setOnFinished { timeline.setOnFinished {
println("Removed: ${tracker}") println("Removed: ${tracker}")
val vbox = model.trackerBoxes.remove(tracker) val vbox = viewModel.trackerBoxes.remove(tracker)
view.sidebar.children.remove(vbox) view.sidebar.children.remove(vbox)
} }
timeline.play() timeline.play()
} }
model.doneTrackers.clear() viewModel.doneTrackers.clear()
} }
} }
} }
@ -322,35 +306,32 @@ class NetworkMapVisualiser : Application() {
} }
} else if (step is ProgressTracker.Change.Structural) { } else if (step is ProgressTracker.Change.Structural) {
val new = view.buildProgressTrackerWidget(widget.label.text, tracker) val new = view.buildProgressTrackerWidget(widget.label.text, tracker)
val prevWidget = model.trackerBoxes[step.tracker] ?: throw AssertionError("No previous widget for tracker: ${step.tracker}") val prevWidget = viewModel.trackerBoxes[step.tracker] ?: throw AssertionError("No previous widget for tracker: ${step.tracker}")
val i = (prevWidget.parent as VBox).children.indexOf(model.trackerBoxes[step.tracker]) val i = (prevWidget.parent as VBox).children.indexOf(viewModel.trackerBoxes[step.tracker])
(prevWidget.parent as VBox).children[i] = new.vbox (prevWidget.parent as VBox).children[i] = new.vbox
model.trackerBoxes[step.tracker] = new.vbox viewModel.trackerBoxes[step.tracker] = new.vbox
} }
} }
} }
//========================
// Controller functions
//========================
var started = false var started = false
private fun startSimulation() { private fun startSimulation() {
if (!started) { if (!started) {
model.simulation.start() viewModel.simulation.start()
started = true started = true
} }
} }
private fun reset() { private fun reset() {
model.simulation.stop() viewModel.simulation.stop()
model.simulation = IRSSimulation(true, false, null) viewModel.simulation = IRSSimulation(true, false, null)
started = false started = false
start(view.stage) start(view.stage)
} }
private fun skipNetworkInitialisation() { private fun skipNetworkInitialisation() {
startSimulation() startSimulation()
while (!model.simulation.networkInitialisationFinished.isDone) { while (!viewModel.simulation.networkInitialisationFinished.isDone) {
iterateSimulation() iterateSimulation()
} }
} }
@ -366,7 +347,7 @@ class NetworkMapVisualiser : Application() {
private fun iterateSimulation() { private fun iterateSimulation() {
// Loop until either we ran out of things to do, or we sent an interesting message. // Loop until either we ran out of things to do, or we sent an interesting message.
while (true) { while (true) {
val transfer: InMemoryMessagingNetwork.MessageTransfer = model.simulation.iterate() ?: break val transfer: InMemoryMessagingNetwork.MessageTransfer = viewModel.simulation.iterate() ?: break
if (transferIsInteresting(transfer)) if (transferIsInteresting(transfer))
break break
else else

View File

@ -21,14 +21,11 @@ import javafx.scene.shape.Polygon
import javafx.scene.text.Font import javafx.scene.text.Font
import javafx.stage.Stage import javafx.stage.Stage
import javafx.util.Duration import javafx.util.Duration
import com.r3cev.corda.netmap.VisualiserModel.Style import com.r3cev.corda.netmap.VisualiserViewModel.Style
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyCodeCombination
data class TrackerWidget(val vbox: VBox, val cursorBox: Pane, val label: Label, val cursor: Polygon) data class TrackerWidget(val vbox: VBox, val cursorBox: Pane, val label: Label, val cursor: Polygon)
internal class VisualiserView() { internal class VisualiserView() {
// Structural elements
lateinit var root: Pane lateinit var root: Pane
lateinit var stage: Stage lateinit var stage: Stage
lateinit var splitter: SplitPane lateinit var splitter: SplitPane
@ -43,12 +40,10 @@ internal class VisualiserView() {
var scrollPane: ScrollPane? = null var scrollPane: ScrollPane? = null
var hideButton = Button("«").apply { styleClass += "hide-sidebar-button" } var hideButton = Button("«").apply { styleClass += "hide-sidebar-button" }
// Content
// -23.2031,29.8406,33.0469,64.3209 // -23.2031,29.8406,33.0469,64.3209
val mapImage = ImageView(Image(NetworkMapVisualiser::class.java.getResourceAsStream("Europe.jpg"))) val mapImage = ImageView(Image(NetworkMapVisualiser::class.java.getResourceAsStream("Europe.jpg")))
// Display properties
val backgroundColor: Color = mapImage.image.pixelReader.getColor(0, 0) val backgroundColor: Color = mapImage.image.pixelReader.getColor(0, 0)
val stageWidth = 1024.0 val stageWidth = 1024.0

View File

@ -13,7 +13,7 @@ import javafx.scene.shape.Line
import javafx.util.Duration import javafx.util.Duration
import java.util.* import java.util.*
class VisualiserModel() { class VisualiserViewModel {
enum class Style { enum class Style {
MAP, CIRCLE; MAP, CIRCLE;