Fix target charging (#1872)

This commit is contained in:
schenlap 2021-12-15 10:58:45 +01:00 • committed by GitHub
parent d690633020
commit fbe6532aa3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 29 deletions

View file

@ -6,7 +6,7 @@
{{ $t("main.vehicleSubline.mincharge", { soc: minSoC }) }}
</span>
</small>
<TargetCharge v-bind="targetCharge" @target-time-updated="targetTimeUpdated" v-if="false" />
<TargetCharge v-bind="targetCharge" @target-time-updated="targetTimeUpdated" />
</div>
</template>

View file

@ -1390,7 +1390,7 @@ func (lp *LoadPoint) Update(sitePower float64, cheap bool, batteryBuffered bool)
}
// target charging
case lp.socTimer.DemandActive() && false:
case lp.socTimer.DemandActive():
targetCurrent := lp.socTimer.Handle()
err = lp.setLimit(targetCurrent, true)

View file

@ -55,6 +55,12 @@ func (lp *LoadPoint) GetTargetSoC() int {
return lp.SoC.Target
}
func (lp *LoadPoint) setTargetSoC(soc int) {
lp.SoC.Target = soc
lp.socTimer.SoC = soc
lp.publish("targetSoC", soc)
}
// SetTargetSoC sets loadpoint charge target soc
func (lp *LoadPoint) SetTargetSoC(soc int) {
lp.Lock()
@ -64,8 +70,7 @@ func (lp *LoadPoint) SetTargetSoC(soc int) {
// apply immediately
if lp.SoC.Target != soc {
lp.SoC.Target = soc
lp.publish("targetSoC", soc)
lp.setTargetSoC(soc)
lp.requestUpdate()
}
}
@ -105,21 +110,20 @@ func (lp *LoadPoint) SetPhases(phases int) error {
}
// SetTargetCharge sets loadpoint charge targetSoC
func (lp *LoadPoint) SetTargetCharge(finishAt time.Time, targetSoC int) {
func (lp *LoadPoint) SetTargetCharge(finishAt time.Time, soc int) {
lp.Lock()
defer lp.Unlock()
lp.log.DEBUG.Printf("set target charge: %d @ %v", targetSoC, finishAt)
lp.log.DEBUG.Printf("set target charge: %d @ %v", soc, finishAt)
// apply immediately
// TODO check reset of targetSoC
lp.publish("targetTime", finishAt)
lp.publish("targetSoC", targetSoC)
if lp.socTimer.Time != finishAt || lp.SoC.Target != soc {
lp.publish("targetTime", finishAt)
lp.socTimer.Time = finishAt
lp.socTimer.Time = finishAt
lp.socTimer.SoC = targetSoC
lp.requestUpdate()
lp.setTargetSoC(soc)
lp.requestUpdate()
}
}
// RemoteControl sets remote status demand

View file

@ -50,6 +50,18 @@ func (s *Estimator) Reset() {
s.energyPerSocStep = s.virtualCapacity / 100
}
// AssumedChargeDuration estimates charge duration up to targetSoC based on virtual capacity
func (s *Estimator) AssumedChargeDuration(targetSoC int, chargePower float64) time.Duration {
percentRemaining := float64(targetSoC) - s.vehicleSoc
if percentRemaining <= 0 {
return 0
}
whRemaining := percentRemaining / 100 * s.virtualCapacity
return time.Duration(float64(time.Hour) * whRemaining / chargePower).Round(time.Second)
}
// RemainingChargeDuration returns the remaining duration estimate based on SoC, target and charge power
func (s *Estimator) RemainingChargeDuration(chargePower float64, targetSoC int) time.Duration {
if chargePower > 0 {
@ -71,9 +83,7 @@ func (s *Estimator) RemainingChargeDuration(chargePower float64, targetSoC int)
}
}
// estimate remaining time
whRemaining := percentRemaining / 100 * s.virtualCapacity
return time.Duration(float64(time.Hour) * whRemaining / chargePower).Round(time.Second)
return s.AssumedChargeDuration(targetSoC, chargePower)
}
return -1

View file

@ -39,9 +39,9 @@ func (lp *Timer) Reset() {
return
}
lp.current = float64(lp.GetMaxCurrent())
lp.current = lp.GetMaxCurrent()
lp.Time = time.Time{}
lp.SoC = 0
lp.active = false
}
// DemandActive calculates remaining charge duration and returns true if charge start is required to achieve target soc in time
@ -50,9 +50,7 @@ func (lp *Timer) DemandActive() bool {
return false
}
se := lp.SocEstimator()
if se == nil {
lp.log.WARN.Printf("target charging: not possible")
if lp.Time.IsZero() {
return false
}
@ -68,14 +66,28 @@ func (lp *Timer) DemandActive() bool {
power *= lp.current / lp.GetMaxCurrent()
}
se := lp.SocEstimator()
if se == nil {
lp.log.WARN.Printf("target charging: not possible")
return false
}
// time
remainingDuration := se.RemainingChargeDuration(power, lp.SoC)
remainingDuration := se.AssumedChargeDuration(lp.SoC, power)
lp.finishAt = time.Now().Add(remainingDuration).Round(time.Minute)
lp.log.DEBUG.Printf("estimated charge duration: %v to %d%% at %.0fW", remainingDuration.Round(time.Minute), lp.SoC, power)
if lp.active {
lp.log.DEBUG.Printf("projected end: %v", lp.finishAt)
lp.log.DEBUG.Printf("desired finish time: %v", lp.Time)
} else {
lp.log.DEBUG.Printf("projected start: %v", lp.Time.Add(-remainingDuration))
}
// timer charging is already active- only deactivate once charging has stopped
if lp.active {
if time.Now().After(lp.Time) && lp.GetStatus() != api.StatusC {
lp.log.TRACE.Printf("target charging: deactivating")
lp.log.INFO.Printf("target charging: deactivating")
lp.active = false
}
@ -85,7 +97,7 @@ func (lp *Timer) DemandActive() bool {
// check if charging need be activated
if lp.active = lp.finishAt.After(lp.Time); lp.active {
lp.current = lp.GetMaxCurrent()
lp.log.DEBUG.Printf("target charging active for %v: projected %v (%v remaining)", lp.Time, lp.finishAt, remainingDuration.Round(time.Minute))
lp.log.INFO.Printf("target charging active for %v: projected %v (%v remaining)", lp.Time, lp.finishAt, remainingDuration.Round(time.Minute))
}
return lp.active

4
dist/index.html vendored
View file

@ -1,4 +1,4 @@
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="EV Charge Controller"><meta name="author" content="andig"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png?[[.Version]]"><link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png?[[.Version]]"><link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png?[[.Version]]"><link rel="manifest" href="ico/site.webmanifest"><link rel="mask-icon" href="ico/safari-pinned-tab.svg?[[.Version]]" color="#18191a"><link rel="shortcut icon" href="ico/favicon.ico?[[.Version]]"><meta name="apple-mobile-web-app-title" content="evcc"><meta name="application-name" content="evcc"><meta name="msapplication-TileColor" content="#18191a"><meta name="msapplication-config" content="ico/browserconfig.xml"><meta name="theme-color" content="#18191a"><title>evcc</title><link href="css/chunk-vendors.a7b690cd.css" rel="preload" as="style"><link href="css/index.31975401.css" rel="preload" as="style"><link href="js/chunk-vendors.6e78ad3a.js" rel="preload" as="script"><link href="js/index.1a5119f8.js" rel="preload" as="script"><link href="css/chunk-vendors.a7b690cd.css" rel="stylesheet"><link href="css/index.31975401.css" rel="stylesheet"></head><body><script>window.evcc = {
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="EV Charge Controller"><meta name="author" content="andig"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png?[[.Version]]"><link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png?[[.Version]]"><link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png?[[.Version]]"><link rel="manifest" href="ico/site.webmanifest"><link rel="mask-icon" href="ico/safari-pinned-tab.svg?[[.Version]]" color="#18191a"><link rel="shortcut icon" href="ico/favicon.ico?[[.Version]]"><meta name="apple-mobile-web-app-title" content="evcc"><meta name="application-name" content="evcc"><meta name="msapplication-TileColor" content="#18191a"><meta name="msapplication-config" content="ico/browserconfig.xml"><meta name="theme-color" content="#18191a"><title>evcc</title><link href="css/chunk-vendors.a7b690cd.css" rel="preload" as="style"><link href="css/index.31975401.css" rel="preload" as="style"><link href="js/chunk-vendors.6e78ad3a.js" rel="preload" as="script"><link href="js/index.c646bc03.js" rel="preload" as="script"><link href="css/chunk-vendors.a7b690cd.css" rel="stylesheet"><link href="css/index.31975401.css" rel="stylesheet"></head><body><script>window.evcc = {
version: "[[.Version]]",
configured: "[[.Configured]]",
};</script><div id="app"></div><script src="js/chunk-vendors.6e78ad3a.js"></script><script src="js/index.1a5119f8.js"></script></body></html>
};</script><div id="app"></div><script src="js/chunk-vendors.6e78ad3a.js"></script><script src="js/index.c646bc03.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/js/index.c646bc03.js.map vendored Normal file

File diff suppressed because one or more lines are too long