Sunspec: refactor model selection (#11933)
This commit is contained in:
parent
fcfceb4937
commit
49bf1e6ec6
6 changed files with 193 additions and 123 deletions
|
|
@ -31,7 +31,7 @@ func init() {
|
|||
func NewModbusSunspecFromConfig(other map[string]interface{}) (Provider, error) {
|
||||
cc := struct {
|
||||
modbus.Settings `mapstructure:",squash"`
|
||||
Value string
|
||||
Value []string
|
||||
Scale float64
|
||||
Delay time.Duration
|
||||
ConnectDelay time.Duration
|
||||
|
|
@ -67,7 +67,7 @@ func NewModbusSunspecFromConfig(other map[string]interface{}) (Provider, error)
|
|||
log := util.NewLogger("sunspec")
|
||||
conn.Logger(log.TRACE)
|
||||
|
||||
if cc.Value == "" {
|
||||
if len(cc.Value) == 0 {
|
||||
return nil, errors.New("value is required")
|
||||
}
|
||||
|
||||
|
|
@ -77,9 +77,13 @@ func NewModbusSunspecFromConfig(other map[string]interface{}) (Provider, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ops, err := modbus.ParsePoint(cc.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid sunspec value: %s", cc.Value)
|
||||
var ops []modbus.SunSpecOperation
|
||||
for _, val := range cc.Value {
|
||||
op, err := modbus.ParsePoint(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid sunspec value: %s", cc.Value)
|
||||
}
|
||||
ops = append(ops, op)
|
||||
}
|
||||
|
||||
mb := &ModbusSunspec{
|
||||
|
|
|
|||
|
|
@ -15,59 +15,104 @@ params:
|
|||
advanced: true
|
||||
render: |
|
||||
{{- if eq .usage "grid" }}
|
||||
type: mbmd
|
||||
{{- include "modbus" . }}
|
||||
type: custom
|
||||
# sunspec model 203 (int+sf)/ 213 (float) meter
|
||||
power: 203|213:W
|
||||
energy: 203|213:TotWhImp
|
||||
power:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 203:W
|
||||
- 213:W
|
||||
energy:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 203:TotWhImp
|
||||
- 213:TotWhImp
|
||||
currents:
|
||||
- 203|213:AphA
|
||||
- 203|213:AphB
|
||||
- 203|213:AphC
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:AphA
|
||||
- 213:AphA
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:AphB
|
||||
- 213:AphB
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:AphC
|
||||
- 213:AphC
|
||||
voltages:
|
||||
- 203|213:PhVphA
|
||||
- 203|213:PhVphB
|
||||
- 203|213:PhVphC
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:PhVphA
|
||||
- 213:PhVphA
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:PhVphB
|
||||
- 213:PhVphB
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:PhVphC
|
||||
- 213:PhVphC
|
||||
powers:
|
||||
- 203|213:WphA
|
||||
- 203|213:WphB
|
||||
- 203|213:WphC
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:WphA
|
||||
- 213:WphA
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:WphB
|
||||
- 213:WphB
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:WphC
|
||||
- 213:WphC
|
||||
{{- end }}
|
||||
{{- if eq .usage "pv" }}
|
||||
type: custom
|
||||
power:
|
||||
source: calc
|
||||
add:
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value: 160:1:DCW # mppt 1
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value: 160:2:DCW # mppt 2
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 6 }}
|
||||
value: 160:1:DCW # mppt 1
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 6 }}
|
||||
value: 160:2:DCW # mppt 2
|
||||
energy:
|
||||
source: calc
|
||||
add:
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value: 160:1:DCWH # mppt 1
|
||||
scale: 0.001
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value: 160:2:DCWH # mppt 2
|
||||
scale: 0.001
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 6 }}
|
||||
value: 160:1:DCWH # mppt 1
|
||||
scale: 0.001
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 6 }}
|
||||
value: 160:2:DCWH # mppt 2
|
||||
scale: 0.001
|
||||
{{- end }}
|
||||
{{- if eq .usage "battery" }}
|
||||
type: custom
|
||||
power:
|
||||
source: calc
|
||||
add:
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value: 160:3:DCW # mppt 3 (charge)
|
||||
scale: -1
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value: 160:4:DCW # mppt 4 (discharge)
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 6 }}
|
||||
value: 160:3:DCW # mppt 3 (charge)
|
||||
scale: -1
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 6 }}
|
||||
value: 160:4:DCW # mppt 4 (discharge)
|
||||
energy:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
|
|
@ -76,8 +121,8 @@ render: |
|
|||
soc:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value: 124:ChaState
|
||||
{{- if .capacity }}
|
||||
value:
|
||||
- 124:ChaState
|
||||
- 802:SoC
|
||||
capacity: {{ .capacity }} # kWh
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -9,63 +9,96 @@ params:
|
|||
choice: ["grid", "pv", "battery"]
|
||||
- name: modbus
|
||||
choice: ["tcpip", "rs485"]
|
||||
- name: integer
|
||||
description:
|
||||
de: Integer Registerzugriff (nur für Netzzähler)
|
||||
en: Integer register access (grid meter only)
|
||||
help:
|
||||
de: Einstellung Float/Integer im Wechselrichter überprüfen
|
||||
en: Verify Float/Integer setting in inverter
|
||||
advanced: true
|
||||
type: bool
|
||||
- name: capacity
|
||||
advanced: true
|
||||
render: |
|
||||
type: mbmd
|
||||
{{- include "modbus" . }}
|
||||
{{- if eq .usage "grid" }}
|
||||
model: sunspec
|
||||
{{- if eq .integer "true" }}
|
||||
# sunspec model 203 (int+sf) meter
|
||||
power: 203:W
|
||||
energy: 203:TotWhImp
|
||||
type: custom
|
||||
# sunspec model 203 (int+sf)/ 213 (float) meter
|
||||
power:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 203:W
|
||||
- 213:W
|
||||
energy:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 203:TotWhImp
|
||||
- 213:TotWhImp
|
||||
currents:
|
||||
- 203:AphA
|
||||
- 203:AphB
|
||||
- 203:AphC
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:AphA
|
||||
- 213:AphA
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:AphB
|
||||
- 213:AphB
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:AphC
|
||||
- 213:AphC
|
||||
voltages:
|
||||
- 203:PhVphA
|
||||
- 203:PhVphB
|
||||
- 203:PhVphC
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:PhVphA
|
||||
- 213:PhVphA
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:PhVphB
|
||||
- 213:PhVphB
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:PhVphC
|
||||
- 213:PhVphC
|
||||
powers:
|
||||
- 203:WphA
|
||||
- 203:WphB
|
||||
- 203:WphC
|
||||
{{ else }}
|
||||
# sunspec model 213 (float) meter
|
||||
power: 213:W
|
||||
energy: 213:TotWhImp
|
||||
currents:
|
||||
- 213:AphA
|
||||
- 213:AphB
|
||||
- 213:AphC
|
||||
voltages:
|
||||
- 213:PhVphA
|
||||
- 213:PhVphB
|
||||
- 213:PhVphC
|
||||
powers:
|
||||
- 213:WphA
|
||||
- 213:WphB
|
||||
- 213:WphC
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:WphA
|
||||
- 213:WphA
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:WphB
|
||||
- 213:WphB
|
||||
- source: sunspec
|
||||
{{- include "modbus" . | indent 4 }}
|
||||
value:
|
||||
- 203:WphC
|
||||
- 213:WphC
|
||||
{{- end }}
|
||||
{{- if eq .usage "pv" }}
|
||||
type: custom
|
||||
power:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 101:W
|
||||
- 103:W
|
||||
energy:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 101:WH
|
||||
- 103:WH
|
||||
scale: 0.001
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
model: sunspec
|
||||
power: Power
|
||||
energy: Export
|
||||
{{- if eq .usage "battery" }}
|
||||
soc: 124:ChaState
|
||||
{{- if .capacity }}
|
||||
type: custom
|
||||
soc:
|
||||
source: sunspec
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
value:
|
||||
- 124:ChaState
|
||||
- 802:SoC
|
||||
capacity: {{ .capacity }} # kWh
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,9 @@ func ParseOperation(dev meters.Device, measurement string) (Operation, error) {
|
|||
// if measurement cannot be parsed it could be SunSpec model/block/point
|
||||
op.MBMD.IEC61850, err = meters.MeasurementString(measurement)
|
||||
if err != nil {
|
||||
suns, err := ParsePoint(measurement)
|
||||
if err == nil {
|
||||
op.SunSpec = suns[0]
|
||||
if op.SunSpec, err = ParsePoint(measurement); err != nil {
|
||||
return op, err
|
||||
}
|
||||
return op, err
|
||||
}
|
||||
|
||||
// for RS485 check if producer supports the measurement
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ import (
|
|||
func TestParsePoint(t *testing.T) {
|
||||
tc := []struct {
|
||||
in string
|
||||
ops []SunSpecOperation
|
||||
ops SunSpecOperation
|
||||
}{
|
||||
{"103:W", []SunSpecOperation{{103, 0, "W"}}},
|
||||
{"802:1:V", []SunSpecOperation{{802, 1, "V"}}},
|
||||
{"101|103:DCW", []SunSpecOperation{{101, 0, "DCW"}, {103, 0, "DCW"}}},
|
||||
{"103:W", SunSpecOperation{103, 0, "W"}},
|
||||
{"802:1:V", SunSpecOperation{802, 1, "V"}},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
|
|
|
|||
|
|
@ -13,39 +13,30 @@ type SunSpecOperation struct {
|
|||
}
|
||||
|
||||
// ParsePoint parses sunspec point from string
|
||||
func ParsePoint(selector string) ([]SunSpecOperation, error) {
|
||||
func ParsePoint(selector string) (SunSpecOperation, error) {
|
||||
var (
|
||||
res SunSpecOperation
|
||||
err error
|
||||
)
|
||||
|
||||
el := strings.Split(selector, ":")
|
||||
if len(el) < 2 || len(el) > 3 {
|
||||
return nil, fmt.Errorf("invalid sunspec format: %s", selector)
|
||||
return res, fmt.Errorf("invalid sunspec format: %s", selector)
|
||||
}
|
||||
|
||||
models := strings.Split(el[0], "|")
|
||||
if len(models) == 0 {
|
||||
return nil, fmt.Errorf("missing sunspec model: %s", selector)
|
||||
if res.Model, err = strconv.Atoi(el[0]); err != nil {
|
||||
return res, fmt.Errorf("invalid sunspec model: %s", selector)
|
||||
}
|
||||
|
||||
var res []SunSpecOperation
|
||||
for _, m := range models {
|
||||
model, err := strconv.Atoi(m)
|
||||
if len(el) == 3 {
|
||||
// block is the middle element
|
||||
res.Block, err = strconv.Atoi(el[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid sunspec model: %s", selector)
|
||||
return res, fmt.Errorf("invalid sunspec block: %s", selector)
|
||||
}
|
||||
|
||||
var block int
|
||||
if len(el) == 3 {
|
||||
// block is the middle element
|
||||
block, err = strconv.Atoi(el[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid sunspec block: %s", selector)
|
||||
}
|
||||
}
|
||||
|
||||
res = append(res, SunSpecOperation{
|
||||
Model: model,
|
||||
Block: block,
|
||||
Point: el[len(el)-1],
|
||||
})
|
||||
}
|
||||
|
||||
res.Point = el[len(el)-1]
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue