MBMD: allow invertering using minus prefix (#26201)
This commit is contained in:
parent
d9b8ba95a7
commit
545f3a758e
2 changed files with 44 additions and 56 deletions
|
|
@ -19,9 +19,7 @@ import (
|
|||
type ModbusMbmd struct {
|
||||
conn *modbus.Connection
|
||||
device *rs485.RS485
|
||||
opPower rs485.Operation
|
||||
opEnergy rs485.Operation
|
||||
opSoc rs485.Operation
|
||||
powerG func() (float64, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -90,7 +88,7 @@ func NewModbusMbmdFromConfig(ctx context.Context, other map[string]any) (api.Met
|
|||
|
||||
ops := device.Producer().Produce()
|
||||
|
||||
m.opPower, err = rs485FindDeviceOp(ops, cc.Power)
|
||||
m.powerG, err = m.deviceOp(ops, cc.Power)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid measurement for power: %s", cc.Power)
|
||||
}
|
||||
|
|
@ -98,12 +96,12 @@ func NewModbusMbmdFromConfig(ctx context.Context, other map[string]any) (api.Met
|
|||
// decorate energy
|
||||
var totalEnergy func() (float64, error)
|
||||
if cc.Energy != "" {
|
||||
m.opEnergy, err = rs485FindDeviceOp(ops, cc.Energy)
|
||||
g, err := m.deviceOp(ops, cc.Energy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid measurement for energy: %s", cc.Energy)
|
||||
}
|
||||
|
||||
totalEnergy = m.totalEnergy
|
||||
totalEnergy = g
|
||||
}
|
||||
|
||||
// decorate currents
|
||||
|
|
@ -127,17 +125,50 @@ func NewModbusMbmdFromConfig(ctx context.Context, other map[string]any) (api.Met
|
|||
// decorate soc
|
||||
var soc func() (float64, error)
|
||||
if cc.Soc != "" {
|
||||
m.opSoc, err = rs485FindDeviceOp(ops, cc.Soc)
|
||||
g, err := m.deviceOp(ops, cc.Soc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid measurement for soc: %s", cc.Soc)
|
||||
}
|
||||
|
||||
soc = m.soc
|
||||
soc = g
|
||||
}
|
||||
|
||||
return decorateModbusMbmd(m, totalEnergy, currentsG, voltagesG, powersG, soc, cc.batteryCapacity.Decorator()), nil
|
||||
}
|
||||
|
||||
// deviceOp checks is RS485 device supports operation
|
||||
func (m *ModbusMbmd) deviceOp(ops []rs485.Operation, name string) (func() (float64, error), error) {
|
||||
// leading minus sign?
|
||||
name, invert := strings.CutPrefix(name, "-")
|
||||
|
||||
measurement, err := meters.MeasurementString(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid measurement: %s", name)
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
if op.IEC61850 == measurement {
|
||||
return func() (float64, error) {
|
||||
res, err := m.device.QueryOp(m.conn, op)
|
||||
|
||||
// silence NaN reading errors by assuming zero
|
||||
if err != nil && errors.Is(err, meters.ErrNaN) {
|
||||
res.Value = 0
|
||||
err = nil
|
||||
}
|
||||
|
||||
if invert {
|
||||
return -res.Value, err
|
||||
}
|
||||
|
||||
return res.Value, err
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported measurement: %s", measurement.String())
|
||||
}
|
||||
|
||||
func (m *ModbusMbmd) buildPhaseProviders(ops []rs485.Operation, readings []string) (func() (float64, float64, float64, error), error) {
|
||||
if len(readings) == 0 {
|
||||
return nil, nil
|
||||
|
|
@ -149,43 +180,18 @@ func (m *ModbusMbmd) buildPhaseProviders(ops []rs485.Operation, readings []strin
|
|||
|
||||
var phases [3]func() (float64, error)
|
||||
for idx, reading := range readings {
|
||||
opCurrent, err := rs485FindDeviceOp(ops, reading)
|
||||
g, err := m.deviceOp(ops, reading)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid measurement [%d]: %s", idx, reading)
|
||||
}
|
||||
|
||||
phases[idx] = func() (float64, error) {
|
||||
return m.floatGetter(opCurrent)
|
||||
}
|
||||
phases[idx] = g
|
||||
}
|
||||
|
||||
return measurement.CombinePhases(phases), nil
|
||||
}
|
||||
|
||||
// floatGetter executes configured modbus read operation and implements func() (float64, error)
|
||||
func (m *ModbusMbmd) floatGetter(op rs485.Operation) (float64, error) {
|
||||
res, err := m.device.QueryOp(m.conn, op)
|
||||
|
||||
// silence NaN reading errors by assuming zero
|
||||
if err != nil && errors.Is(err, meters.ErrNaN) {
|
||||
res.Value = 0
|
||||
err = nil
|
||||
}
|
||||
|
||||
return res.Value, err
|
||||
}
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
func (m *ModbusMbmd) CurrentPower() (float64, error) {
|
||||
return m.floatGetter(m.opPower)
|
||||
}
|
||||
|
||||
// totalEnergy implements the api.MeterEnergy interface
|
||||
func (m *ModbusMbmd) totalEnergy() (float64, error) {
|
||||
return m.floatGetter(m.opEnergy)
|
||||
}
|
||||
|
||||
// soc implements the api.Battery interface
|
||||
func (m *ModbusMbmd) soc() (float64, error) {
|
||||
return m.floatGetter(m.opSoc)
|
||||
return m.powerG()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/volkszaehler/mbmd/meters"
|
||||
"github.com/volkszaehler/mbmd/meters/rs485"
|
||||
)
|
||||
|
||||
|
|
@ -17,19 +15,3 @@ func isRS485(model string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// rs485FindDeviceOp checks is RS485 device supports operation
|
||||
func rs485FindDeviceOp(ops []rs485.Operation, name string) (op rs485.Operation, err error) {
|
||||
measurement, err := meters.MeasurementString(name)
|
||||
if err != nil {
|
||||
return rs485.Operation{}, fmt.Errorf("invalid measurement: %s", name)
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
if op.IEC61850 == measurement {
|
||||
return op, nil
|
||||
}
|
||||
}
|
||||
|
||||
return op, fmt.Errorf("unsupported measurement: %s", measurement.String())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue