engine refactor
This commit is contained in:
parent
9e663db9dc
commit
81b8e763bd
37 changed files with 3532 additions and 1639 deletions
|
|
@ -4,14 +4,11 @@ import (
|
|||
"fmt"
|
||||
|
||||
"predictor-refactored/internal/engine"
|
||||
"predictor-refactored/internal/weather"
|
||||
)
|
||||
|
||||
// buildProfile translates a PredictionRequest into an engine.Profile.
|
||||
//
|
||||
// elev may be nil when no terrain dataset is loaded; TerrainContact constraints
|
||||
// will return an error in that case.
|
||||
func buildProfile(req PredictionRequest, field weather.WindField, elev engine.TerrainProvider, warnings *engine.Warnings) (engine.Profile, error) {
|
||||
// buildProfile translates a PredictionRequest into an engine.Profile via
|
||||
// the engine registry.
|
||||
func buildProfile(req PredictionRequest, deps engine.BuildDeps) (engine.Profile, error) {
|
||||
if len(req.Profile) == 0 {
|
||||
return engine.Profile{}, fmt.Errorf("profile must contain at least one stage")
|
||||
}
|
||||
|
|
@ -37,24 +34,27 @@ func buildProfile(req PredictionRequest, field weather.WindField, elev engine.Te
|
|||
|
||||
props := make([]*engine.Propagator, len(req.Profile))
|
||||
for i, stage := range req.Profile {
|
||||
model, err := buildModel(stage.Model, field, warnings)
|
||||
if err != nil {
|
||||
return engine.Profile{}, fmt.Errorf("stage %q: %w", stage.Name, err)
|
||||
if stage.Name == "" {
|
||||
return engine.Profile{}, fmt.Errorf("stage %d: name is required", i)
|
||||
}
|
||||
constraints, err := buildConstraints(stage.Constraints, elev)
|
||||
built, err := engine.BuildModel(stage.Model, deps)
|
||||
if err != nil {
|
||||
return engine.Profile{}, fmt.Errorf("stage %q model: %w", stage.Name, err)
|
||||
}
|
||||
constraints, err := buildConstraintList(stage.Constraints, deps)
|
||||
if err != nil {
|
||||
return engine.Profile{}, fmt.Errorf("stage %q: %w", stage.Name, err)
|
||||
}
|
||||
props[i] = &engine.Propagator{
|
||||
Name: stage.Name,
|
||||
Step: step,
|
||||
Model: model,
|
||||
Model: built.Model,
|
||||
BuildModel: built.Build,
|
||||
Constraints: constraints,
|
||||
Tolerance: tol,
|
||||
}
|
||||
}
|
||||
|
||||
// Wire fallbacks once all stages exist.
|
||||
for i, stage := range req.Profile {
|
||||
if stage.FallbackIndex == nil {
|
||||
continue
|
||||
|
|
@ -66,80 +66,22 @@ func buildProfile(req PredictionRequest, field weather.WindField, elev engine.Te
|
|||
props[i].Fallback = props[idx]
|
||||
}
|
||||
|
||||
return engine.Profile{Stages: props, Direction: dir}, nil
|
||||
}
|
||||
|
||||
func buildModel(spec ModelSpec, field weather.WindField, warnings *engine.Warnings) (engine.Model, error) {
|
||||
var base engine.Model
|
||||
switch spec.Type {
|
||||
case "constant_rate":
|
||||
base = engine.ConstantRate(spec.Rate)
|
||||
case "parachute_descent":
|
||||
if spec.SeaLevelRate <= 0 {
|
||||
return nil, fmt.Errorf("parachute_descent requires positive sea_level_rate")
|
||||
}
|
||||
base = engine.ParachuteDescent(spec.SeaLevelRate)
|
||||
case "piecewise":
|
||||
segs := make([]engine.RateSegment, len(spec.Segments))
|
||||
for i, s := range spec.Segments {
|
||||
segs[i] = engine.RateSegment{Until: s.Until, Rate: s.Rate}
|
||||
}
|
||||
base = engine.Piecewise(segs)
|
||||
case "wind":
|
||||
if field == nil {
|
||||
return nil, fmt.Errorf("wind model requires a loaded dataset")
|
||||
}
|
||||
return engine.WindTransport(field, warnings), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown model type %q", spec.Type)
|
||||
globals, err := buildConstraintList(req.Globals, deps)
|
||||
if err != nil {
|
||||
return engine.Profile{}, fmt.Errorf("globals: %w", err)
|
||||
}
|
||||
|
||||
if spec.IncludeWind {
|
||||
if field == nil {
|
||||
return nil, fmt.Errorf("include_wind requires a loaded dataset")
|
||||
}
|
||||
return engine.Sum(base, engine.WindTransport(field, warnings)), nil
|
||||
}
|
||||
return base, nil
|
||||
return engine.Profile{Stages: props, Direction: dir, Globals: globals}, nil
|
||||
}
|
||||
|
||||
func buildConstraints(specs []ConstraintSpec, elev engine.TerrainProvider) ([]engine.Constraint, error) {
|
||||
func buildConstraintList(specs []engine.ConstraintSpec, deps engine.BuildDeps) ([]engine.Constraint, error) {
|
||||
out := make([]engine.Constraint, 0, len(specs))
|
||||
for _, spec := range specs {
|
||||
action, err := parseAction(spec.Action)
|
||||
for i, spec := range specs {
|
||||
c, err := engine.BuildConstraint(spec, deps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var c engine.Constraint
|
||||
switch spec.Type {
|
||||
case "max_altitude":
|
||||
c = engine.MaxAltitude{Limit: spec.Limit, On: action}
|
||||
case "min_altitude":
|
||||
c = engine.MinAltitude{Limit: spec.Limit, On: action}
|
||||
case "max_time":
|
||||
c = engine.MaxTime{Limit: spec.Limit, On: action}
|
||||
case "terrain_contact":
|
||||
if elev == nil {
|
||||
return nil, fmt.Errorf("terrain_contact requires an elevation dataset")
|
||||
}
|
||||
c = engine.TerrainContact{Provider: elev, On: action}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown constraint type %q", spec.Type)
|
||||
return nil, fmt.Errorf("constraint[%d]: %w", i, err)
|
||||
}
|
||||
out = append(out, c)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseAction(s string) (engine.Action, error) {
|
||||
switch s {
|
||||
case "", "stop":
|
||||
return engine.ActionStop, nil
|
||||
case "fallback":
|
||||
return engine.ActionFallback, nil
|
||||
case "clip":
|
||||
return engine.ActionClip, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown constraint action %q", s)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue