mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Testdata: add basic tank simulation (#47990)
This commit is contained in:
parent
ce89d7e874
commit
72b5af8d9b
@ -66,6 +66,7 @@ func NewSimulationEngine() (*SimulationEngine, error) {
|
||||
initializers := []simulationInitializer{
|
||||
newFlightSimInfo,
|
||||
newSinewaveInfo,
|
||||
newTankSimInfo,
|
||||
}
|
||||
|
||||
for _, init := range initializers {
|
||||
@ -155,7 +156,10 @@ func (s *SimulationEngine) QueryData(ctx context.Context, req *backend.QueryData
|
||||
maxPoints := q.MaxDataPoints * 2
|
||||
for i := int64(0); i < maxPoints && timeWalkerMs < to; i++ {
|
||||
t := time.UnixMilli(timeWalkerMs).UTC()
|
||||
appendFrameRow(frame, sim.GetValues(t))
|
||||
vals := sim.GetValues(t)
|
||||
if vals != nil { // nil is returned when you ask for an invalid time
|
||||
appendFrameRow(frame, vals)
|
||||
}
|
||||
timeWalkerMs += stepMillis
|
||||
}
|
||||
}
|
||||
|
152
pkg/tsdb/testdatasource/sims/tank.go
Normal file
152
pkg/tsdb/testdatasource/sims/tank.go
Normal file
@ -0,0 +1,152 @@
|
||||
package sims
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
)
|
||||
|
||||
type tankSim struct {
|
||||
key simulationKey
|
||||
cfg tankConfig
|
||||
state tankState
|
||||
}
|
||||
|
||||
var (
|
||||
_ Simulation = (*tankSim)(nil)
|
||||
)
|
||||
|
||||
type tankConfig struct {
|
||||
TankCapacity float64 `json:"tankCapacity"` // size (in gallons) of the tank
|
||||
FillRate float64 `json:"fillRate"` // fillRate
|
||||
DrainRate float64 `json:"drainRate"` // how fast it will drain (when valve is open)
|
||||
DrainOpen bool `json:"drainOpen"`
|
||||
PumpOn bool `json:"pumpOn"`
|
||||
}
|
||||
|
||||
type tankState struct {
|
||||
Time time.Time
|
||||
FillPercent float64 // 0-1
|
||||
}
|
||||
|
||||
func (s *tankSim) GetState() simulationState {
|
||||
return simulationState{
|
||||
Key: s.key,
|
||||
Config: s.cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *tankSim) SetConfig(vals map[string]interface{}) error {
|
||||
return updateConfigObjectFromJSON(&s.cfg, vals)
|
||||
}
|
||||
|
||||
func (s *tankSim) NewFrame(size int) *data.Frame {
|
||||
frame := data.NewFrameOfFieldTypes("", size,
|
||||
data.FieldTypeTime, // time
|
||||
data.FieldTypeFloat64, // value
|
||||
data.FieldTypeFloat64, // value
|
||||
data.FieldTypeBool,
|
||||
data.FieldTypeBool,
|
||||
)
|
||||
|
||||
var min data.ConfFloat64 = 0.0
|
||||
var max data.ConfFloat64 = 1.0
|
||||
|
||||
frame.Fields[0].Name = "time"
|
||||
frame.Fields[1].Name = "percentFull"
|
||||
frame.Fields[1].Config = &data.FieldConfig{
|
||||
Unit: "percentunit",
|
||||
Min: &min,
|
||||
Max: &max,
|
||||
}
|
||||
|
||||
frame.Fields[2].Name = "tankVolume"
|
||||
frame.Fields[2].Config = &data.FieldConfig{
|
||||
Unit: "m3", // cubic meters
|
||||
}
|
||||
frame.Fields[3].Name = "pumpOn"
|
||||
frame.Fields[4].Name = "drainOpen"
|
||||
return frame
|
||||
}
|
||||
|
||||
func (s *tankSim) GetValues(t time.Time) map[string]interface{} {
|
||||
if t.Before(s.state.Time) {
|
||||
return nil // can not look backwards!
|
||||
}
|
||||
if t.After(s.state.Time) {
|
||||
elapsed := t.Sub(s.state.Time).Seconds()
|
||||
v := s.state.FillPercent * s.cfg.TankCapacity
|
||||
if s.cfg.PumpOn {
|
||||
v += (elapsed * s.cfg.FillRate)
|
||||
}
|
||||
if s.cfg.DrainOpen {
|
||||
v -= (elapsed * s.cfg.DrainRate)
|
||||
}
|
||||
|
||||
p := v / s.cfg.TankCapacity
|
||||
if p > 1 {
|
||||
p = 1
|
||||
} else if p < 0 {
|
||||
p = 0
|
||||
}
|
||||
|
||||
s.state.FillPercent = p
|
||||
s.state.Time = t
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"time": s.state.Time,
|
||||
"percentFull": s.state.FillPercent,
|
||||
"tankVolume": s.state.FillPercent * s.cfg.TankCapacity,
|
||||
"pumpOn": s.cfg.PumpOn,
|
||||
"drainOpen": s.cfg.DrainOpen,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *tankSim) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newTankSimInfo() simulationInfo {
|
||||
tc := tankConfig{
|
||||
TankCapacity: 100.0,
|
||||
FillRate: 1.0, // gallons/min?
|
||||
DrainRate: 0.5, // `json:"drainRate,omitempty"` // how fast it will drain (when valve is open)
|
||||
DrainOpen: false,
|
||||
PumpOn: true,
|
||||
}
|
||||
ts := tankState{
|
||||
Time: time.Now(),
|
||||
FillPercent: .6,
|
||||
}
|
||||
|
||||
df := data.NewFrame("")
|
||||
df.Fields = append(df.Fields, data.NewField("tankCapacity", nil, []float64{tc.TankCapacity}).SetConfig(&data.FieldConfig{
|
||||
Unit: "m3",
|
||||
}))
|
||||
df.Fields = append(df.Fields, data.NewField("fillRate", nil, []float64{tc.FillRate}).SetConfig(&data.FieldConfig{
|
||||
Unit: "cms",
|
||||
}))
|
||||
df.Fields = append(df.Fields, data.NewField("drainRate", nil, []float64{tc.DrainRate}).SetConfig(&data.FieldConfig{
|
||||
Unit: "cms",
|
||||
}))
|
||||
df.Fields = append(df.Fields, data.NewField("drainOpen", nil, []bool{tc.DrainOpen}))
|
||||
df.Fields = append(df.Fields, data.NewField("pumpOn", nil, []bool{tc.PumpOn}))
|
||||
|
||||
return simulationInfo{
|
||||
Type: "tank",
|
||||
Name: "Tank",
|
||||
Description: "Fill and drain a water tank",
|
||||
SetupFields: df,
|
||||
OnlyForward: false,
|
||||
create: func(cfg simulationState) (Simulation, error) {
|
||||
s := &tankSim{
|
||||
key: cfg.Key,
|
||||
cfg: tc,
|
||||
state: ts,
|
||||
}
|
||||
err := updateConfigObjectFromJSON(&s.cfg, cfg.Config) // override any fields
|
||||
return s, err
|
||||
},
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ export const SimulationQueryEditor = ({ onChange, query }: EditorProps) => {
|
||||
const options = [
|
||||
{ label: 'Flight', value: 'flight' },
|
||||
{ label: 'Sine', value: 'sine' },
|
||||
{ label: 'Tank', value: 'tank' },
|
||||
];
|
||||
|
||||
const onUpdateKey = (key: typeof simQuery.key) => {
|
||||
|
Loading…
Reference in New Issue
Block a user