implement some features of Venus OS
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
332
mk2driver/metadata.go
Normal file
332
mk2driver/metadata.go
Normal file
@@ -0,0 +1,332 @@
|
||||
package mk2driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTransactionRetries = 2
|
||||
defaultTransactionRetryDelay = 200 * time.Millisecond
|
||||
defaultTransactionBackoff = 1.5
|
||||
fastCommandTimeout = 1500 * time.Millisecond
|
||||
standardCommandTimeout = 3 * time.Second
|
||||
slowCommandTimeout = 6 * time.Second
|
||||
)
|
||||
|
||||
type registerKey struct {
|
||||
kind RegisterKind
|
||||
id uint16
|
||||
}
|
||||
|
||||
func int16Ptr(v int16) *int16 {
|
||||
return &v
|
||||
}
|
||||
|
||||
var knownRegisterMetadata = map[registerKey]RegisterMetadata{
|
||||
{kind: RegisterKindRAMVar, id: ramVarVMains}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarVMains,
|
||||
Name: "mains_voltage",
|
||||
Description: "AC input mains voltage (scaled)",
|
||||
Unit: "V",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarIMains}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarIMains,
|
||||
Name: "mains_current",
|
||||
Description: "AC input mains current (scaled)",
|
||||
Unit: "A",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarVInverter}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarVInverter,
|
||||
Name: "inverter_voltage",
|
||||
Description: "AC output inverter voltage (scaled)",
|
||||
Unit: "V",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarIInverter}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarIInverter,
|
||||
Name: "inverter_current",
|
||||
Description: "AC output inverter current (scaled)",
|
||||
Unit: "A",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarVBat}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarVBat,
|
||||
Name: "battery_voltage",
|
||||
Description: "Battery voltage (scaled)",
|
||||
Unit: "V",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarIBat}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarIBat,
|
||||
Name: "battery_current",
|
||||
Description: "Battery current (scaled)",
|
||||
Unit: "A",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarVBatRipple}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarVBatRipple,
|
||||
Name: "battery_voltage_ripple",
|
||||
Description: "Battery ripple voltage (scaled)",
|
||||
Unit: "V",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarInverterPeriod}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarInverterPeriod,
|
||||
Name: "inverter_period",
|
||||
Description: "Inverter period source value",
|
||||
Unit: "ticks",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarMainPeriod}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarMainPeriod,
|
||||
Name: "mains_period",
|
||||
Description: "Mains period source value",
|
||||
Unit: "ticks",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarIACLoad}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarIACLoad,
|
||||
Name: "ac_load_current",
|
||||
Description: "AC load current (scaled)",
|
||||
Unit: "A",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarVirSwitchPos}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarVirSwitchPos,
|
||||
Name: "virtual_switch_position",
|
||||
Description: "Virtual switch position",
|
||||
Unit: "state",
|
||||
Scale: 1,
|
||||
Writable: true,
|
||||
Signed: false,
|
||||
MinValue: int16Ptr(0),
|
||||
MaxValue: int16Ptr(255),
|
||||
SafetyClass: RegisterSafetyGuarded,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarIgnACInState}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarIgnACInState,
|
||||
Name: "ignored_ac_input_state",
|
||||
Description: "AC input state as seen by firmware",
|
||||
Unit: "state",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: false,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarMultiFuncRelay}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarMultiFuncRelay,
|
||||
Name: "multifunction_relay",
|
||||
Description: "Multifunction relay state",
|
||||
Unit: "state",
|
||||
Scale: 1,
|
||||
Writable: true,
|
||||
Signed: false,
|
||||
MinValue: int16Ptr(0),
|
||||
MaxValue: int16Ptr(1),
|
||||
SafetyClass: RegisterSafetyOperational,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarChargeState}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarChargeState,
|
||||
Name: "battery_charge_state",
|
||||
Description: "Battery charge state fraction",
|
||||
Unit: "fraction",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarInverterPower1}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarInverterPower1,
|
||||
Name: "inverter_power_1",
|
||||
Description: "Inverter power source register 1",
|
||||
Unit: "W",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarInverterPower2}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarInverterPower2,
|
||||
Name: "inverter_power_2",
|
||||
Description: "Inverter power source register 2",
|
||||
Unit: "W",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
{kind: RegisterKindRAMVar, id: ramVarOutPower}: {
|
||||
Kind: RegisterKindRAMVar,
|
||||
ID: ramVarOutPower,
|
||||
Name: "output_power",
|
||||
Description: "Output power source register",
|
||||
Unit: "W",
|
||||
Scale: 1,
|
||||
Writable: false,
|
||||
Signed: true,
|
||||
SafetyClass: RegisterSafetyReadOnly,
|
||||
},
|
||||
}
|
||||
|
||||
func normalizeTransactionOptions(opts TransactionOptions) TransactionOptions {
|
||||
if opts.Retries < 0 {
|
||||
opts.Retries = 0
|
||||
}
|
||||
if opts.RetryDelay < 0 {
|
||||
opts.RetryDelay = 0
|
||||
}
|
||||
if opts.BackoffFactor <= 0 {
|
||||
opts.BackoffFactor = defaultTransactionBackoff
|
||||
}
|
||||
if opts.Retries == 0 && opts.RetryDelay == 0 && !opts.ReadBeforeWrite && !opts.VerifyAfterWrite {
|
||||
opts.Retries = defaultTransactionRetries
|
||||
opts.RetryDelay = defaultTransactionRetryDelay
|
||||
opts.BackoffFactor = defaultTransactionBackoff
|
||||
opts.ReadBeforeWrite = true
|
||||
opts.VerifyAfterWrite = true
|
||||
opts.TimeoutClass = TimeoutClassStandard
|
||||
return opts
|
||||
}
|
||||
if opts.RetryDelay == 0 {
|
||||
opts.RetryDelay = defaultTransactionRetryDelay
|
||||
}
|
||||
if opts.TimeoutClass == "" {
|
||||
opts.TimeoutClass = TimeoutClassStandard
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func lookupRegisterMetadata(kind RegisterKind, id uint16) (RegisterMetadata, bool) {
|
||||
meta, ok := knownRegisterMetadata[registerKey{kind: kind, id: id}]
|
||||
if ok {
|
||||
meta = withMetadataDefaults(meta)
|
||||
}
|
||||
return meta, ok
|
||||
}
|
||||
|
||||
func listRegisterMetadata() []RegisterMetadata {
|
||||
out := make([]RegisterMetadata, 0, len(knownRegisterMetadata))
|
||||
for _, meta := range knownRegisterMetadata {
|
||||
out = append(out, withMetadataDefaults(meta))
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].Kind != out[j].Kind {
|
||||
return out[i].Kind < out[j].Kind
|
||||
}
|
||||
return out[i].ID < out[j].ID
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
func validateValueAgainstMetadata(meta RegisterMetadata, value int16) error {
|
||||
if meta.MinValue != nil && value < *meta.MinValue {
|
||||
return fmt.Errorf("value %d is below minimum %d for %s:%d", value, *meta.MinValue, meta.Kind, meta.ID)
|
||||
}
|
||||
if meta.MaxValue != nil && value > *meta.MaxValue {
|
||||
return fmt.Errorf("value %d is above maximum %d for %s:%d", value, *meta.MaxValue, meta.Kind, meta.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func withMetadataDefaults(meta RegisterMetadata) RegisterMetadata {
|
||||
if meta.Scale == 0 {
|
||||
meta.Scale = 1
|
||||
}
|
||||
if meta.SafetyClass == "" {
|
||||
if meta.Writable {
|
||||
meta.SafetyClass = RegisterSafetyGuarded
|
||||
} else {
|
||||
meta.SafetyClass = RegisterSafetyReadOnly
|
||||
}
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
func resolveCommandTimeout(opts TransactionOptions) time.Duration {
|
||||
if opts.CommandTimeout > 0 {
|
||||
return opts.CommandTimeout
|
||||
}
|
||||
switch opts.TimeoutClass {
|
||||
case TimeoutClassFast:
|
||||
return fastCommandTimeout
|
||||
case TimeoutClassSlow:
|
||||
return slowCommandTimeout
|
||||
default:
|
||||
return standardCommandTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func retryDelayForAttempt(opts TransactionOptions, attempt int) time.Duration {
|
||||
if opts.RetryDelay <= 0 || attempt <= 1 {
|
||||
return opts.RetryDelay
|
||||
}
|
||||
factor := math.Pow(opts.BackoffFactor, float64(attempt-1))
|
||||
delay := float64(opts.RetryDelay) * factor
|
||||
return time.Duration(delay)
|
||||
}
|
||||
|
||||
func defaultWritableRegisterAddresses() []RegisterAddress {
|
||||
metas := listRegisterMetadata()
|
||||
out := make([]RegisterAddress, 0, len(metas))
|
||||
for _, meta := range metas {
|
||||
if !meta.Writable {
|
||||
continue
|
||||
}
|
||||
out = append(out, RegisterAddress{
|
||||
Kind: meta.Kind,
|
||||
ID: meta.ID,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user