Planner: add precondition phase before departure (#20431)
This commit is contained in:
parent
c4be8e918d
commit
832012ef15
41 changed files with 746 additions and 222 deletions
|
|
@ -9,6 +9,7 @@ import (
|
|||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// pass converts a simple api without return value to api with nil error return value
|
||||
|
|
@ -28,6 +29,21 @@ func parseFloat(payload string) (float64, error) {
|
|||
return f, err
|
||||
}
|
||||
|
||||
// parseDuration parses a duration string as seconds
|
||||
func parseDuration(payload string) (time.Duration, error) {
|
||||
if payload == "" {
|
||||
return 0, nil
|
||||
}
|
||||
v, err := strconv.Atoi(payload)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if v < 0 {
|
||||
return 0, fmt.Errorf("invalid duration: %s", payload)
|
||||
}
|
||||
return time.Duration(v) * time.Second, err
|
||||
}
|
||||
|
||||
// jsonDecoder returns a json decoder with disallowed unknown fields
|
||||
func jsonDecoder(r io.Reader) *json.Decoder {
|
||||
dec := json.NewDecoder(r)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func getLoadpointStaticConfig(lp loadpoint.API) loadpoint.StaticConfig {
|
|||
}
|
||||
|
||||
func getLoadpointDynamicConfig(lp loadpoint.API) loadpoint.DynamicConfig {
|
||||
planTime, planEnergy := lp.GetPlanEnergy()
|
||||
planTime, planPrecondition, planEnergy := lp.GetPlanEnergy()
|
||||
return loadpoint.DynamicConfig{
|
||||
Title: lp.GetTitle(),
|
||||
DefaultMode: string(lp.GetDefaultMode()),
|
||||
|
|
@ -40,6 +40,7 @@ func getLoadpointDynamicConfig(lp loadpoint.API) loadpoint.DynamicConfig {
|
|||
Soc: lp.GetSocConfig(),
|
||||
PlanEnergy: planEnergy,
|
||||
PlanTime: planTime,
|
||||
PlanPrecondition: int64(planPrecondition.Seconds()),
|
||||
LimitEnergy: lp.GetLimitEnergy(),
|
||||
LimitSoc: lp.GetLimitSoc(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,21 +49,24 @@ func planHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
id := lp.EffectivePlanId()
|
||||
|
||||
goal, _ := lp.GetPlanGoal()
|
||||
precondition := lp.GetPlanPreCondDuration()
|
||||
requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower)
|
||||
plan := lp.GetPlan(planTime, requiredDuration)
|
||||
plan := lp.GetPlan(planTime, requiredDuration, precondition)
|
||||
|
||||
res := struct {
|
||||
PlanId int `json:"planId"`
|
||||
PlanTime time.Time `json:"planTime"`
|
||||
Duration int64 `json:"duration"`
|
||||
Plan api.Rates `json:"plan"`
|
||||
Power float64 `json:"power"`
|
||||
PlanId int `json:"planId"`
|
||||
PlanTime time.Time `json:"planTime"`
|
||||
Duration int64 `json:"duration"`
|
||||
Precondition int64 `json:"precondition"`
|
||||
Plan api.Rates `json:"plan"`
|
||||
Power float64 `json:"power"`
|
||||
}{
|
||||
PlanId: id,
|
||||
PlanTime: planTime,
|
||||
Duration: int64(requiredDuration.Seconds()),
|
||||
Plan: plan,
|
||||
Power: maxPower,
|
||||
PlanId: id,
|
||||
PlanTime: planTime,
|
||||
Duration: int64(requiredDuration.Seconds()),
|
||||
Precondition: int64(precondition.Seconds()),
|
||||
Plan: plan,
|
||||
Power: maxPower,
|
||||
}
|
||||
|
||||
jsonResult(w, res)
|
||||
|
|
@ -74,6 +77,7 @@ func planHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
query := r.URL.Query()
|
||||
|
||||
planTime, err := time.ParseInLocation(time.RFC3339, vars["time"], nil)
|
||||
if err != nil {
|
||||
|
|
@ -87,6 +91,12 @@ func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
precondition, err := parseDuration(query.Get("precondition"))
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
switch typ := vars["type"]; typ {
|
||||
case "soc":
|
||||
if !lp.SocBasedPlanning() {
|
||||
|
|
@ -105,18 +115,20 @@ func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
|
||||
maxPower := lp.EffectiveMaxPower()
|
||||
requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower)
|
||||
plan := lp.GetPlan(planTime, requiredDuration)
|
||||
plan := lp.GetPlan(planTime, requiredDuration, precondition)
|
||||
|
||||
res := struct {
|
||||
PlanTime time.Time `json:"planTime"`
|
||||
Duration int64 `json:"duration"`
|
||||
Plan api.Rates `json:"plan"`
|
||||
Power float64 `json:"power"`
|
||||
PlanTime time.Time `json:"planTime"`
|
||||
Duration int64 `json:"duration"`
|
||||
Precondition int64 `json:"precondition"`
|
||||
Plan api.Rates `json:"plan"`
|
||||
Power float64 `json:"power"`
|
||||
}{
|
||||
PlanTime: planTime,
|
||||
Duration: int64(requiredDuration.Seconds()),
|
||||
Plan: plan,
|
||||
Power: maxPower,
|
||||
PlanTime: planTime,
|
||||
Duration: int64(requiredDuration.Seconds()),
|
||||
Precondition: int64(precondition.Seconds()),
|
||||
Plan: plan,
|
||||
Power: maxPower,
|
||||
}
|
||||
|
||||
jsonResult(w, res)
|
||||
|
|
@ -126,6 +138,7 @@ func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
func repeatingPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
query := r.URL.Query()
|
||||
|
||||
hourMinute := vars["time"]
|
||||
tz := vars["tz"]
|
||||
|
|
@ -152,20 +165,28 @@ func repeatingPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
precondition, err := parseDuration(query.Get("precondition"))
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
maxPower := lp.EffectiveMaxPower()
|
||||
requiredDuration := lp.GetPlanRequiredDuration(soc, maxPower)
|
||||
plan := lp.GetPlan(planTime, requiredDuration)
|
||||
plan := lp.GetPlan(planTime, requiredDuration, precondition)
|
||||
|
||||
res := struct {
|
||||
PlanTime time.Time `json:"planTime"`
|
||||
Duration int64 `json:"duration"`
|
||||
Plan api.Rates `json:"plan"`
|
||||
Power float64 `json:"power"`
|
||||
PlanTime time.Time `json:"planTime"`
|
||||
Duration int64 `json:"duration"`
|
||||
Precondition int64 `json:"precondition"`
|
||||
Plan api.Rates `json:"plan"`
|
||||
Power float64 `json:"power"`
|
||||
}{
|
||||
PlanTime: planTime,
|
||||
Duration: int64(requiredDuration.Seconds()),
|
||||
Plan: plan,
|
||||
Power: maxPower,
|
||||
PlanTime: planTime,
|
||||
Duration: int64(requiredDuration.Seconds()),
|
||||
Precondition: int64(precondition.Seconds()),
|
||||
Plan: plan,
|
||||
Power: maxPower,
|
||||
}
|
||||
|
||||
jsonResult(w, res)
|
||||
|
|
@ -176,6 +197,7 @@ func repeatingPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
func planEnergyHandler(lp loadpoint.API) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
query := r.URL.Query()
|
||||
|
||||
ts, err := time.ParseInLocation(time.RFC3339, vars["time"], nil)
|
||||
if err != nil {
|
||||
|
|
@ -189,19 +211,27 @@ func planEnergyHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
if err := lp.SetPlanEnergy(ts, val); err != nil {
|
||||
precondition, err := parseDuration(query.Get("precondition"))
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
ts, energy := lp.GetPlanEnergy()
|
||||
if err := lp.SetPlanEnergy(ts, precondition, val); err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
ts, precondition, energy := lp.GetPlanEnergy()
|
||||
|
||||
res := struct {
|
||||
Energy float64 `json:"energy"`
|
||||
Time time.Time `json:"time"`
|
||||
Energy float64 `json:"energy"`
|
||||
Precondition int64 `json:"precondition"`
|
||||
Time time.Time `json:"time"`
|
||||
}{
|
||||
Energy: energy,
|
||||
Time: ts,
|
||||
Energy: energy,
|
||||
Precondition: int64(precondition.Seconds()),
|
||||
Time: ts,
|
||||
}
|
||||
|
||||
jsonResult(w, res)
|
||||
|
|
@ -211,7 +241,7 @@ func planEnergyHandler(lp loadpoint.API) http.HandlerFunc {
|
|||
// planRemoveHandler removes plan time
|
||||
func planRemoveHandler(lp loadpoint.API) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := lp.SetPlanEnergy(time.Time{}, 0); err != nil {
|
||||
if err := lp.SetPlanEnergy(time.Time{}, 0, 0); err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ func limitSocHandler(site site.API) http.HandlerFunc {
|
|||
func planSocHandler(site site.API) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
query := r.URL.Query()
|
||||
|
||||
v, err := site.Vehicles().ByName(vars["name"])
|
||||
if err != nil {
|
||||
|
|
@ -92,19 +93,27 @@ func planSocHandler(site site.API) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
if err := v.SetPlanSoc(ts, soc); err != nil {
|
||||
precondition, err := parseDuration(query.Get("precondition"))
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
ts, soc = v.GetPlanSoc()
|
||||
if err := v.SetPlanSoc(ts, precondition, soc); err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
ts, precondition, soc = v.GetPlanSoc()
|
||||
|
||||
res := struct {
|
||||
Soc int `json:"soc"`
|
||||
Time time.Time `json:"time"`
|
||||
Soc int `json:"soc"`
|
||||
Precondition int64 `json:"precondition"`
|
||||
Time time.Time `json:"time"`
|
||||
}{
|
||||
Soc: soc,
|
||||
Time: ts,
|
||||
Soc: soc,
|
||||
Precondition: int64(precondition.Seconds()),
|
||||
Time: ts,
|
||||
}
|
||||
|
||||
jsonResult(w, res)
|
||||
|
|
@ -152,7 +161,7 @@ func planSocRemoveHandler(site site.API) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
if err := v.SetPlanSoc(time.Time{}, 0); err != nil {
|
||||
if err := v.SetPlanSoc(time.Time{}, 0, 0); err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,12 +244,13 @@ func (m *MQTT) listenLoadpointSetters(topic string, site site.API, lp loadpoint.
|
|||
{"batteryBoost", boolSetter(lp.SetBatteryBoost)},
|
||||
{"planEnergy", func(payload string) error {
|
||||
var plan struct {
|
||||
Time time.Time `json:"time"`
|
||||
Value float64 `json:"value"`
|
||||
Time time.Time `json:"time"`
|
||||
Precondition int64 `json:"precondition"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
err := json.Unmarshal([]byte(payload), &plan)
|
||||
if err == nil {
|
||||
err = lp.SetPlanEnergy(plan.Time, plan.Value)
|
||||
err = lp.SetPlanEnergy(plan.Time, time.Duration(plan.Precondition)*time.Second, plan.Value)
|
||||
}
|
||||
return err
|
||||
}},
|
||||
|
|
@ -280,12 +281,13 @@ func (m *MQTT) listenVehicleSetters(topic string, v vehicle.API) error {
|
|||
{"minSoc", intSetter(pass(v.SetMinSoc))},
|
||||
{"planSoc", func(payload string) error {
|
||||
var plan struct {
|
||||
Time time.Time `json:"time"`
|
||||
Value int `json:"value"`
|
||||
Time time.Time `json:"time"`
|
||||
Precondition int64 `json:"precondition"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
err := json.Unmarshal([]byte(payload), &plan)
|
||||
if err == nil {
|
||||
err = v.SetPlanSoc(plan.Time, plan.Value)
|
||||
err = v.SetPlanSoc(plan.Time, time.Duration(plan.Precondition)*time.Second, plan.Value)
|
||||
}
|
||||
return err
|
||||
}},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue