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

View File

@@ -463,6 +463,110 @@ func Test_mk2Ser_WriteRAMVarByID(t *testing.T) {
assert.Equal(t, expected, writeBuffer.Bytes())
}
func Test_mk2Ser_RegisterMetadata(t *testing.T) {
m := &mk2Ser{}
meta, ok := m.RegisterMetadata(RegisterKindRAMVar, ramVarVBat)
assert.True(t, ok)
assert.Equal(t, RegisterKindRAMVar, meta.Kind)
assert.Equal(t, uint16(ramVarVBat), meta.ID)
assert.Equal(t, "battery_voltage", meta.Name)
assert.False(t, meta.Writable)
_, ok = m.RegisterMetadata(RegisterKindSetting, 9999)
assert.False(t, ok)
all := m.ListRegisterMetadata()
assert.NotEmpty(t, all)
}
func Test_mk2Ser_WriteRegister_Verified(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
p: testIO,
writeAck: make(chan byte, 2),
winmonAck: make(chan winmonResponse, 4),
}
go func() {
time.Sleep(10 * time.Millisecond)
// Read-before-write response.
m.pushWinmonResponse(commandReadSettingResponse, []byte{0x01, 0x00})
time.Sleep(10 * time.Millisecond)
// Write acknowledgement.
m.pushWriteResponse(commandWriteViaIDResponse)
time.Sleep(10 * time.Millisecond)
// Verify-after-write response.
m.pushWinmonResponse(commandReadSettingResponse, []byte{0x34, 0x12})
}()
result, err := m.WriteRegister(RegisterKindSetting, 0x0042, 0x1234, TransactionOptions{
ReadBeforeWrite: true,
VerifyAfterWrite: true,
})
assert.NoError(t, err)
assert.Equal(t, 1, result.Attempts)
if assert.NotNil(t, result.PreviousValue) {
assert.Equal(t, int16(1), *result.PreviousValue)
}
if assert.NotNil(t, result.VerifiedValue) {
assert.Equal(t, int16(0x1234), *result.VerifiedValue)
}
expected := append([]byte{}, buildSentCommand(winmonFrame, commandReadSetting, 0x42, 0x00)...)
expected = append(expected, buildSentCommand(winmonFrame, commandWriteViaID, 0x42, 0x00, 0x34, 0x12)...)
expected = append(expected, buildSentCommand(winmonFrame, commandReadSetting, 0x42, 0x00)...)
assert.Equal(t, expected, writeBuffer.Bytes())
}
func Test_mk2Ser_WriteRegister_VerifyRetry(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
p: testIO,
writeAck: make(chan byte, 4),
winmonAck: make(chan winmonResponse, 8),
}
go func() {
time.Sleep(10 * time.Millisecond)
m.pushWriteResponse(commandWriteViaIDResponse)
time.Sleep(10 * time.Millisecond)
// First verify mismatch.
m.pushWinmonResponse(commandReadSettingResponse, []byte{0x00, 0x00})
time.Sleep(10 * time.Millisecond)
m.pushWriteResponse(commandWriteViaIDResponse)
time.Sleep(10 * time.Millisecond)
// Second verify matches expected value.
m.pushWinmonResponse(commandReadSettingResponse, []byte{0x78, 0x56})
}()
result, err := m.WriteRegister(RegisterKindSetting, 0x0042, 0x5678, TransactionOptions{
Retries: 1,
RetryDelay: 1 * time.Millisecond,
VerifyAfterWrite: true,
})
assert.NoError(t, err)
assert.Equal(t, 2, result.Attempts)
if assert.NotNil(t, result.VerifiedValue) {
assert.Equal(t, int16(0x5678), *result.VerifiedValue)
}
}
func Test_mk2Ser_WriteRegister_ReadOnlyMetadata(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
p: testIO,
writeAck: make(chan byte, 1),
}
_, err := m.WriteRegister(RegisterKindRAMVar, ramVarVBat, 1, TransactionOptions{
VerifyAfterWrite: true,
})
assert.Error(t, err)
assert.ErrorContains(t, err, "read-only")
assert.Empty(t, writeBuffer.Bytes())
}
func Test_mk2Ser_GetDeviceState(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
@@ -726,6 +830,149 @@ func Test_mk2Ser_SetStandby_Disabled(t *testing.T) {
assert.Equal(t, expected, writeBuffer.Bytes())
}
func Test_mk2Ser_WriteSettingBySelection(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
p: testIO,
writeAck: make(chan byte, 1),
}
go func() {
time.Sleep(10 * time.Millisecond)
m.pushWriteResponse(commandWriteSettingResponse)
}()
err := m.WriteSettingBySelection(0x0020, 0x0011)
assert.NoError(t, err)
expected := append([]byte{}, buildSentCommand(winmonFrame, commandWriteSetting, 0x20, 0x00)...)
expected = append(expected, buildSentCommand(winmonFrame, commandWriteData, 0x11, 0x00)...)
assert.Equal(t, expected, writeBuffer.Bytes())
}
func Test_mk2Ser_WriteSelectedData(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
p: testIO,
writeAck: make(chan byte, 1),
}
go func() {
time.Sleep(10 * time.Millisecond)
m.pushWriteResponse(commandWriteRAMResponse)
}()
err := m.WriteSelectedData(0x0022)
assert.NoError(t, err)
expected := buildSentCommand(winmonFrame, commandWriteData, 0x22, 0x00)
assert.Equal(t, expected, writeBuffer.Bytes())
}
func Test_mk2Ser_CaptureAndDiffSnapshot(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
p: testIO,
winmonAck: make(chan winmonResponse, 2),
}
go func() {
time.Sleep(10 * time.Millisecond)
m.pushWinmonResponse(commandReadRAMResponse, []byte{0x03, 0x00})
time.Sleep(10 * time.Millisecond)
m.pushWinmonResponse(commandReadRAMResponse, []byte{0x04, 0x00})
}()
addresses := []RegisterAddress{
{Kind: RegisterKindRAMVar, ID: ramVarVirSwitchPos},
}
snapshot, err := m.CaptureSnapshot(addresses)
assert.NoError(t, err)
if assert.Len(t, snapshot.Entries, 1) {
assert.Equal(t, RegisterSafetyGuarded, snapshot.Entries[0].Safety)
assert.Equal(t, int16(3), snapshot.Entries[0].Value)
}
diffs, err := m.DiffSnapshot(snapshot)
assert.NoError(t, err)
if assert.Len(t, diffs, 1) {
assert.True(t, diffs[0].Changed)
assert.Equal(t, int16(4), diffs[0].Current)
assert.Equal(t, int16(3), diffs[0].Target)
}
}
func Test_mk2Ser_RestoreSnapshot(t *testing.T) {
testIO := NewIOStub(nil)
m := &mk2Ser{
p: testIO,
winmonAck: make(chan winmonResponse, 4),
writeAck: make(chan byte, 2),
}
go func() {
time.Sleep(10 * time.Millisecond)
// DiffSnapshot current value.
m.pushWinmonResponse(commandReadRAMResponse, []byte{0x04, 0x00})
time.Sleep(10 * time.Millisecond)
// WriteRegister read-before-write.
m.pushWinmonResponse(commandReadRAMResponse, []byte{0x04, 0x00})
time.Sleep(10 * time.Millisecond)
// Write ack.
m.pushWriteResponse(commandWriteRAMViaIDResponse)
time.Sleep(10 * time.Millisecond)
// Verify-after-write.
m.pushWinmonResponse(commandReadRAMResponse, []byte{0x03, 0x00})
}()
result, err := m.RestoreSnapshot(RegisterSnapshot{
Entries: []RegisterSnapshotEntry{
{
Kind: RegisterKindRAMVar,
ID: ramVarVirSwitchPos,
Value: 3,
Writable: true,
},
},
}, TransactionOptions{
ReadBeforeWrite: true,
VerifyAfterWrite: true,
RetryDelay: 1 * time.Millisecond,
})
assert.NoError(t, err)
assert.False(t, result.RolledBack)
if assert.Len(t, result.Applied, 1) {
if assert.NotNil(t, result.Applied[0].PreviousValue) {
assert.Equal(t, int16(4), *result.Applied[0].PreviousValue)
}
}
}
func Test_mk2Ser_DriverDiagnostics(t *testing.T) {
m := &mk2Ser{
traceLimit: 10,
traces: make([]ProtocolTrace, 0, 10),
}
m.appendTrace(ProtocolTrace{
Timestamp: time.Now().UTC(),
Direction: TraceDirectionTX,
Frame: "0x57",
Command: "winmon:0x30",
BytesHex: "AA",
})
m.noteCommandFailure(assert.AnError)
m.noteCommandTimeout(assert.AnError)
m.checksumErrors.Add(1)
m.markFrameSeen()
diag := m.DriverDiagnostics(10)
assert.NotEmpty(t, diag.Traces)
assert.Greater(t, diag.CommandFailures, uint64(0))
assert.Greater(t, diag.CommandTimeouts, uint64(0))
assert.Greater(t, diag.ChecksumFailures, uint64(0))
assert.GreaterOrEqual(t, diag.HealthScore, 0)
}
func Test_parseFrameLength(t *testing.T) {
tests := []struct {
name string