implement some features of Venus OS
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-02-19 15:37:41 +11:00
parent d72e88ab7b
commit e8153e2953
21 changed files with 4143 additions and 90 deletions

332
mk2driver/metadata.go Normal file
View 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
}