Testdata: add basic tank simulation (#47990)

This commit is contained in:
Ryan McKinley 2022-04-20 12:01:09 -07:00 committed by GitHub
parent ce89d7e874
commit 72b5af8d9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 158 additions and 1 deletions

View File

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

View 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
},
}
}

View File

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