whispervis/widgets/simulation.go

184 lines
3.9 KiB
Go
Raw Normal View History

2018-09-19 16:00:53 +03:00
package widgets
import (
2018-10-24 19:37:31 +02:00
"fmt"
2018-09-19 16:00:53 +03:00
"github.com/gopherjs/vecty"
"github.com/gopherjs/vecty/elem"
"github.com/gopherjs/vecty/event"
"github.com/gopherjs/vecty/prop"
)
2018-10-24 17:37:39 +02:00
const (
DefaultTTL = 10 // seconds
)
2018-09-19 16:00:53 +03:00
// Simulation represents configuration panel for propagation simulation.
type Simulation struct {
vecty.Core
2018-10-27 09:45:12 +02:00
startSimulation func() (int, error)
2018-10-19 16:01:49 +02:00
replay func()
2018-10-27 09:45:12 +02:00
step func(int)
2018-09-19 16:00:53 +03:00
2018-10-27 09:45:12 +02:00
address string // backend host address
timeline *Range
2018-10-24 17:37:39 +02:00
ttl int
errMsg string
hasResults bool
inProgress bool
2018-09-19 16:00:53 +03:00
}
// NewSimulation creates new simulation configuration panel. If simulation
// backend host address is not specified, it'll use 'localhost:8084' as a default.
2018-10-27 09:45:12 +02:00
func NewSimulation(address string, startSimulation func() (int, error), replay func(), step func(int)) *Simulation {
2018-09-19 16:00:53 +03:00
if address == "" {
address = "http://localhost:8084"
}
return &Simulation{
2018-10-24 17:37:39 +02:00
ttl: DefaultTTL,
2018-10-19 16:01:49 +02:00
address: address,
startSimulation: startSimulation,
replay: replay,
2018-10-27 09:45:12 +02:00
step: step,
2018-09-19 16:00:53 +03:00
}
}
// Render implements vecty.Component interface for Simulation.
func (s *Simulation) Render() vecty.ComponentOrHTML {
2018-10-20 14:47:19 +02:00
return Widget(
2018-09-19 16:00:53 +03:00
elem.Div(
2018-10-19 22:10:56 +02:00
Header("Simulation backend:"),
2018-10-24 17:37:39 +02:00
InputField("Host:", "Simulation backend host address",
elem.Input(
vecty.Markup(
2018-10-19 22:10:56 +02:00
vecty.MarkupIf(s.inProgress,
vecty.Attribute("disabled", "true"),
),
2018-10-24 17:37:39 +02:00
vecty.Class("input", "is-small"),
prop.Value(s.address),
2018-10-24 19:37:31 +02:00
event.Input(s.onAddressChange),
),
),
),
InputField("TTL:", "Message time to live value (in seconds)",
elem.Input(
vecty.Markup(
vecty.MarkupIf(s.inProgress,
vecty.Attribute("disabled", "true"),
),
vecty.Class("input", "is-small"),
prop.Value(fmt.Sprint(s.ttl)),
event.Input(s.onTTLChange),
),
2018-09-19 16:00:53 +03:00
),
),
),
elem.Div(
2018-09-19 16:00:53 +03:00
elem.Button(
vecty.Markup(
vecty.MarkupIf(s.inProgress,
2018-10-19 22:10:56 +02:00
vecty.Attribute("disabled", "true"),
vecty.Class("is-loading"),
),
2018-10-19 22:10:56 +02:00
vecty.Class("button", "is-info", "is-small"),
2018-09-19 16:00:53 +03:00
event.Click(s.onSimulateClick),
),
vecty.Text("Start simulation"),
),
vecty.If(s.hasResults,
elem.Button(
vecty.Markup(
2018-10-19 22:10:56 +02:00
vecty.Class("button", "is-success", "is-small"),
event.Click(s.onRestartClick),
),
vecty.Text("Replay"),
2018-09-20 22:04:13 +03:00
),
2018-10-27 09:45:12 +02:00
s.timeline,
),
elem.Break(),
2018-10-19 22:10:56 +02:00
vecty.If(s.inProgress, elem.Div(
vecty.Markup(
vecty.Class("notification", "is-success"),
),
vecty.Text("Running simulation..."),
)),
elem.Div(
2018-10-19 22:10:56 +02:00
vecty.If(s.errMsg != "", elem.Div(
vecty.Markup(
2018-10-19 22:10:56 +02:00
vecty.Class("notification", "is-danger"),
),
vecty.Text(s.errMsg),
)),
2018-09-20 22:04:13 +03:00
),
2018-09-19 16:00:53 +03:00
),
)
}
2018-10-24 19:37:31 +02:00
func (s *Simulation) onAddressChange(event *vecty.Event) {
2018-09-19 16:00:53 +03:00
value := event.Target.Get("value").String()
s.address = value
}
2018-10-24 19:37:31 +02:00
func (s *Simulation) onTTLChange(event *vecty.Event) {
value := event.Target.Get("value").Int()
s.ttl = value
}
2018-09-19 16:00:53 +03:00
// Address returns the current backend address.
func (s *Simulation) Address() string {
return s.address
}
2018-10-24 19:37:31 +02:00
// TTL returns the current TTL value.
func (s *Simulation) TTL() int {
return s.ttl
}
2018-09-19 16:00:53 +03:00
func (s *Simulation) onSimulateClick(e *vecty.Event) {
go func() {
s.errMsg = ""
s.hasResults = false
s.inProgress = true
vecty.Rerender(s)
2018-10-27 09:45:12 +02:00
steps, err := s.startSimulation()
if err != nil {
s.errMsg = err.Error()
}
s.hasResults = err == nil
s.inProgress = false
2018-10-27 09:45:12 +02:00
if s.hasResults {
s.timeline = NewRange("Time", "", 0, 0, steps-1, s.step)
}
vecty.Rerender(s)
}()
2018-09-19 21:06:30 +03:00
}
2018-09-20 22:04:13 +03:00
func (s *Simulation) onRestartClick(e *vecty.Event) {
2018-10-19 16:01:49 +02:00
go s.replay()
2018-09-19 16:00:53 +03:00
}
2018-10-24 12:54:48 +02:00
func (s *Simulation) Reset() {
s.hasResults = false
s.inProgress = false
s.errMsg = ""
vecty.Rerender(s)
}
2018-10-27 10:01:15 +02:00
// StepForward increases timeline step.
func (s *Simulation) StepForward() {
s.timeline.Inc()
}
// StepBackward decreases timeline step.
func (s *Simulation) StepBackward() {
s.timeline.Dec()
}