All checks were successful
continuous-integration/drone/push Build is passing
779 lines
21 KiB
Go
779 lines
21 KiB
Go
package mk2driver
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
var knownWrites = []byte{
|
|
0x04, 0xff, 0x41, 0x01, 0x00, 0xbb,
|
|
0x05, 0xff, 0x57, 0x36, 0x00, 0x00, 0x6f,
|
|
0x05, 0xff, 0x57, 0x36, 0x01, 0x00, 0x6e,
|
|
0x05, 0xff, 0x57, 0x36, 0x02, 0x00, 0x6d,
|
|
0x05, 0xff, 0x57, 0x36, 0x03, 0x00, 0x6c,
|
|
0x05, 0xff, 0x57, 0x36, 0x04, 0x00, 0x6b,
|
|
0x05, 0xff, 0x57, 0x36, 0x05, 0x00, 0x6a,
|
|
0x05, 0xff, 0x57, 0x36, 0x06, 0x00, 0x69,
|
|
0x05, 0xff, 0x57, 0x36, 0x07, 0x00, 0x68,
|
|
0x05, 0xff, 0x57, 0x36, 0x08, 0x00, 0x67,
|
|
0x05, 0xff, 0x57, 0x36, 0x09, 0x00, 0x66,
|
|
0x05, 0xff, 0x57, 0x36, 0x0a, 0x00, 0x65,
|
|
0x05, 0xff, 0x57, 0x36, 0x0b, 0x00, 0x64,
|
|
0x05, 0xff, 0x57, 0x36, 0x0c, 0x00, 0x63,
|
|
0x05, 0xff, 0x57, 0x36, 0x0d, 0x00, 0x62,
|
|
0x03, 0xff, 0x46, 0x00, 0xb8,
|
|
0x03, 0xff, 0x46, 0x01, 0xb7,
|
|
0x02, 0xff, 0x4c, 0xb3,
|
|
0x05, 0xff, 0x57, 0x30, 0x0d, 0x00, 0x68,
|
|
}
|
|
|
|
var writeBuffer = bytes.NewBuffer(nil)
|
|
|
|
const (
|
|
testDelta = 0.00000001
|
|
)
|
|
|
|
type testIo struct {
|
|
io.Reader
|
|
io.Writer
|
|
}
|
|
|
|
func NewIOStub(readBuffer []byte) io.ReadWriter {
|
|
writeBuffer = bytes.NewBuffer(nil)
|
|
return &testIo{
|
|
Reader: bytes.NewBuffer(readBuffer),
|
|
Writer: writeBuffer,
|
|
}
|
|
}
|
|
|
|
func buildTestFrame(frameType byte, payload ...byte) (byte, []byte) {
|
|
length := byte(len(payload) + 1)
|
|
sum := int(length) + int(frameType)
|
|
for _, b := range payload {
|
|
sum += int(b)
|
|
}
|
|
checksum := byte((-sum) & 0xff)
|
|
frame := append([]byte{frameType}, payload...)
|
|
frame = append(frame, checksum)
|
|
return length, frame
|
|
}
|
|
|
|
func buildSentCommand(payload ...byte) []byte {
|
|
length := byte(len(payload) + 1)
|
|
sum := int(length) + int(frameHeader)
|
|
out := make([]byte, 0, len(payload)+3)
|
|
out = append(out, length, frameHeader)
|
|
for _, b := range payload {
|
|
sum += int(b)
|
|
out = append(out, b)
|
|
}
|
|
out = append(out, byte((-sum)&0xff))
|
|
return out
|
|
}
|
|
|
|
// Test a know sequence as reference as extracted from Mk2
|
|
func TestSync(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
knownReadBuffer []byte
|
|
knownWrites []byte
|
|
result Mk2Info
|
|
}{
|
|
{
|
|
name: "basic",
|
|
knownReadBuffer: []byte{
|
|
//Len Cmd
|
|
0x04, 0xff, 0x41, 0x01, 0x00, 0xbb,
|
|
0x07, 0xff, 0x56, 0x96, 0x3e, 0x11, 0x00, 0x00, 0xbf,
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
|
|
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1,
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
|
|
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1,
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
|
|
0x08, 0xff, 0x57, 0x8e, 0x57, 0x78, 0x8f, 0x00, 0x01, 0xb5,
|
|
0x08, 0xff, 0x57, 0x8e, 0x2f, 0x7c, 0x8f, 0x00, 0x00, 0xda,
|
|
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1,
|
|
0x08, 0xff, 0x57, 0x8e, 0x04, 0x00, 0x8f, 0x00, 0x80, 0x01,
|
|
0x08, 0xff, 0x57, 0x8e, 0x01, 0x00, 0x8f, 0x00, 0x80, 0x04,
|
|
0x08, 0xff, 0x57, 0x8e, 0x02, 0x00, 0x8f, 0x00, 0x80, 0x03,
|
|
0x08, 0xff, 0x57, 0x8e, 0x38, 0x7f, 0x8f, 0x00, 0x00, 0xce,
|
|
0x07, 0xff, 0x56, 0x96, 0x3e, 0x11, 0x00, 0x00, 0xbf,
|
|
0x0f, 0x20, 0xf3, 0x00, 0xc8, 0x02, 0x0c, 0xa1, 0x05, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x88, 0xb2,
|
|
0x0f, 0x20, 0x01, 0x01, 0xca, 0x09, 0x08, 0xaa, 0x58, 0xab, 0x00, 0xaa, 0x58, 0x9a, 0x00, 0xc3, 0xe8,
|
|
0x06, 0xff, 0x4c, 0x03, 0x00, 0x00, 0x00, 0xac,
|
|
0x05, 0xff, 0x57, 0x85, 0xc8, 0x00, 0x58,
|
|
},
|
|
knownWrites: []byte{
|
|
0x04, 0xff, 0x41, 0x01, 0x00, 0xbb,
|
|
0x05, 0xff, 0x57, 0x36, 0x00, 0x00, 0x6f,
|
|
0x05, 0xff, 0x57, 0x36, 0x01, 0x00, 0x6e,
|
|
0x05, 0xff, 0x57, 0x36, 0x02, 0x00, 0x6d,
|
|
0x05, 0xff, 0x57, 0x36, 0x03, 0x00, 0x6c,
|
|
0x05, 0xff, 0x57, 0x36, 0x04, 0x00, 0x6b,
|
|
0x05, 0xff, 0x57, 0x36, 0x05, 0x00, 0x6a,
|
|
0x05, 0xff, 0x57, 0x36, 0x06, 0x00, 0x69,
|
|
0x05, 0xff, 0x57, 0x36, 0x07, 0x00, 0x68,
|
|
0x05, 0xff, 0x57, 0x36, 0x08, 0x00, 0x67,
|
|
0x05, 0xff, 0x57, 0x36, 0x09, 0x00, 0x66,
|
|
0x05, 0xff, 0x57, 0x36, 0x0a, 0x00, 0x65,
|
|
0x05, 0xff, 0x57, 0x36, 0x0b, 0x00, 0x64,
|
|
0x05, 0xff, 0x57, 0x36, 0x0c, 0x00, 0x63,
|
|
0x05, 0xff, 0x57, 0x36, 0x0d, 0x00, 0x62,
|
|
0x03, 0xff, 0x46, 0x00, 0xb8,
|
|
0x03, 0xff, 0x46, 0x01, 0xb7,
|
|
0x02, 0xff, 0x4c, 0xb3,
|
|
0x05, 0xff, 0x57, 0x30, 0x0d, 0x00, 0x68,
|
|
},
|
|
result: Mk2Info{
|
|
Version: uint32(2736),
|
|
BatVoltage: 14.41,
|
|
BatCurrent: -0.4,
|
|
InVoltage: 226.98,
|
|
InCurrent: 1.71,
|
|
InFrequency: 50.10256410256411,
|
|
OutVoltage: 226.980,
|
|
OutCurrent: 1.54,
|
|
OutFrequency: 50.025510204081634,
|
|
ChargeState: 1,
|
|
LEDs: map[Led]LEDstate{
|
|
LedMain: LedOn,
|
|
LedAbsorption: LedOn,
|
|
LedBulk: LedOff,
|
|
LedFloat: LedOff,
|
|
LedInverter: LedOff,
|
|
LedOverload: LedOff,
|
|
LedLowBattery: LedOff,
|
|
LedTemperature: LedOff,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multiplus24/3000",
|
|
knownReadBuffer: []byte{
|
|
//Len Cmd
|
|
0x04, 0xff, 0x41, 0x01, 0x00, 0xbb,
|
|
0x07, 0xff, 0x56, 0x98, 0x3e, 0x11, 0x00, 0x00, 0xbd, // version
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x0, 0x0, 0x6a, // scale 0
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x0, 0x0, 0x6a, // scale 1
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x0, 0x0, 0x6a, // scale 2
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x0, 0x0, 0x6a, // scale 3
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x0, 0x0, 0x6a, // scale 4
|
|
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x0, 0x0, 0xa1, // scale 5
|
|
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x0, 0x0, 0x6a, // scale 6
|
|
0x08, 0xff, 0x57, 0x8e, 0x57, 0x78, 0x8f, 0x0, 0x1, 0xb5, // scale 7
|
|
0x08, 0xff, 0x57, 0x8e, 0x2f, 0x7c, 0x8f, 0x0, 0x0, 0xda, // scale 8
|
|
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x0, 0x0, 0xa1, //scale 9
|
|
0x08, 0xff, 0x57, 0x8e, 0x4, 0x0, 0x8f, 0x0, 0x80, 0x1, // scale 10
|
|
0x08, 0xff, 0x57, 0x8e, 0x1, 0x0, 0x8f, 0x0, 0x80, 0x4, // scale 11
|
|
0x08, 0xff, 0x57, 0x8e, 0x6, 0x0, 0x8f, 0x0, 0x80, 0xff, // scale 12
|
|
0x08, 0xff, 0x57, 0x8e, 0x38, 0x7f, 0x8f, 0x0, 0x0, 0xce, // scale 13
|
|
0x07, 0xff, 0x56, 0x98, 0x3e, 0x11, 0x0, 0x0, 0xbd, // version
|
|
0x0f, 0x20, 0xb6, 0x89, 0x6d, 0xb7, 0xc, 0x4e, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88, 0x82, // dc info
|
|
0x0f, 0x20, 0x1, 0x1, 0x6d, 0xb7, 0x8, 0x77, 0x5b, 0x21, 0x0, 0x77, 0x5b, 0xfe, 0xff, 0xc3, 0x1e, // ac info
|
|
0x08, 0xff, 0x4c, 0x9, 0x0, 0x0, 0x0, 0x3, 0x0, 0xa1,
|
|
0x05, 0xff, 0x57, 0x85, 0xc8, 0x0, 0x58,
|
|
},
|
|
knownWrites: []byte{},
|
|
result: Mk2Info{
|
|
Version: 0xac0,
|
|
BatVoltage: 26.38,
|
|
BatCurrent: 0,
|
|
InVoltage: 234.15,
|
|
InCurrent: 0.33,
|
|
InFrequency: 50.1025641025641,
|
|
OutVoltage: 234.15,
|
|
OutCurrent: -0.02,
|
|
OutFrequency: 50.025510204081634,
|
|
ChargeState: 1,
|
|
LEDs: map[Led]LEDstate{
|
|
LedMain: LedOn,
|
|
LedAbsorption: LedOff,
|
|
LedBulk: LedOff,
|
|
LedFloat: LedOn,
|
|
LedInverter: LedOff,
|
|
LedOverload: LedOff,
|
|
LedLowBattery: LedOff,
|
|
LedTemperature: LedOff,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
testIO := NewIOStub(tt.knownReadBuffer)
|
|
mk2, err := NewMk2Connection(testIO)
|
|
assert.NoError(t, err, "Could not open MK2")
|
|
|
|
event := <-mk2.C()
|
|
mk2.Close()
|
|
|
|
if len(tt.knownWrites) > 0 {
|
|
assert.Equal(t, 0, bytes.Compare(writeBuffer.Bytes(), knownWrites), "Expected writes did not match received writes")
|
|
}
|
|
assert.True(t, event.Valid, "data not valid")
|
|
assert.Equal(t, tt.result.Version, event.Version, "Invalid version decoded")
|
|
assert.Equal(t, 0, len(event.Errors), "Reported errors not empty")
|
|
assert.Equal(t, tt.result.LEDs, event.LEDs, "Reported LEDs incorrect")
|
|
|
|
assert.InDelta(t, tt.result.BatVoltage, event.BatVoltage, testDelta, "BatVoltage conversion failed")
|
|
assert.InDelta(t, tt.result.BatCurrent, event.BatCurrent, testDelta, "BatCurrent conversion failed")
|
|
assert.InDelta(t, tt.result.InVoltage, event.InVoltage, testDelta, "InVoltage conversion failed")
|
|
assert.InDelta(t, tt.result.InCurrent, event.InCurrent, testDelta, "InCurrent conversion failed")
|
|
assert.InDelta(t, tt.result.InFrequency, event.InFrequency, testDelta, "InFrequency conversion failed")
|
|
assert.InDelta(t, tt.result.OutVoltage, event.OutVoltage, testDelta, "OutVoltage conversion failed")
|
|
assert.InDelta(t, tt.result.OutCurrent, event.OutCurrent, testDelta, "OutCurrent conversion failed")
|
|
assert.InDelta(t, tt.result.OutFrequency, event.OutFrequency, testDelta, "OutFrequency conversion failed")
|
|
assert.InDelta(t, tt.result.ChargeState, event.ChargeState, testDelta, "ChargeState conversion failed")
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_mk2Ser_scaleDecode(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
frame []byte
|
|
expectedScaling scaling
|
|
}{
|
|
{
|
|
name: "Valid scale",
|
|
frame: []byte{0x8e, 0x9c, 0x7f, 0x8f, 0x01, 0x00, 0x6a},
|
|
expectedScaling: scaling{
|
|
scale: 0.01,
|
|
offset: 1,
|
|
supported: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Unsupported frame",
|
|
frame: []byte{0x00},
|
|
expectedScaling: scaling{
|
|
supported: false,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &mk2Ser{
|
|
scales: make([]scaling, 0, ramVarMaxOffset),
|
|
p: NewIOStub([]byte{}),
|
|
}
|
|
m.scaleDecode(tt.frame)
|
|
assert.Equal(t, 1, len(m.scales))
|
|
assert.Equal(t, 1, m.scaleCount)
|
|
assert.Equal(t, tt.expectedScaling.supported, m.scales[0].supported)
|
|
assert.Equal(t, tt.expectedScaling.signed, m.scales[0].signed)
|
|
if tt.expectedScaling.supported {
|
|
assert.InDelta(t, tt.expectedScaling.offset, m.scales[0].offset, testDelta)
|
|
assert.InDelta(t, tt.expectedScaling.scale, m.scales[0].scale, testDelta)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_mk2Ser_calcFreq(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
scales []scaling
|
|
data byte
|
|
scaleIndex int
|
|
want float64
|
|
}{
|
|
{
|
|
name: "Calculate working low",
|
|
scales: []scaling{
|
|
{supported: false},
|
|
},
|
|
data: 0x01,
|
|
scaleIndex: 0,
|
|
want: 10,
|
|
},
|
|
{
|
|
name: "Calculate working high",
|
|
scales: []scaling{
|
|
{
|
|
supported: true,
|
|
offset: 0,
|
|
scale: 0.01,
|
|
},
|
|
},
|
|
data: 0xFE,
|
|
scaleIndex: 0,
|
|
want: 3.937007874015748,
|
|
},
|
|
{
|
|
name: "Calculate clip high",
|
|
scales: []scaling{
|
|
{supported: false},
|
|
},
|
|
data: 0xff,
|
|
scaleIndex: 0,
|
|
want: 0,
|
|
},
|
|
{
|
|
name: "Calculate clip low",
|
|
scales: []scaling{
|
|
{supported: false},
|
|
},
|
|
data: 0x00,
|
|
scaleIndex: 0,
|
|
want: 0,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &mk2Ser{
|
|
scales: tt.scales,
|
|
}
|
|
got := m.calcFreq(tt.data, tt.scaleIndex)
|
|
assert.InDelta(t, tt.want, got, testDelta)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_mk2Ser_WriteSetting(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
writeAck: make(chan byte, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandWriteViaIDResponse)
|
|
}()
|
|
|
|
err := m.WriteSetting(0x1234, 1234)
|
|
assert.NoError(t, err)
|
|
|
|
expected := []byte{
|
|
0x07, 0xff, 0x57, 0x37, 0x34, 0x12, 0xd2, 0x04, 0x50,
|
|
}
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_WriteSetting_FallbackLegacy(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
writeAck: make(chan byte, 2),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandUnsupportedResponse)
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandWriteSettingResponse)
|
|
}()
|
|
|
|
err := m.WriteSetting(0x1234, 1234)
|
|
assert.NoError(t, err)
|
|
|
|
expected := []byte{
|
|
0x07, 0xff, 0x57, 0x37, 0x34, 0x12, 0xd2, 0x04, 0x50,
|
|
0x05, 0xff, 0x57, 0x33, 0x34, 0x12, 0x2c,
|
|
0x05, 0xff, 0x57, 0x34, 0xd2, 0x04, 0x9b,
|
|
}
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_WriteRAMVar(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
writeAck: make(chan byte, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandWriteRAMViaIDResponse)
|
|
}()
|
|
|
|
err := m.WriteRAMVar(0x000d, 1)
|
|
assert.NoError(t, err)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandWriteRAMViaID, 0x0d, 0x00, 0x01, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_WriteRAMVar_FallbackLegacy(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
writeAck: make(chan byte, 2),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandUnsupportedResponse)
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandWriteRAMResponse)
|
|
}()
|
|
|
|
err := m.WriteRAMVar(0x000d, 1)
|
|
assert.NoError(t, err)
|
|
|
|
expected := append([]byte{}, buildSentCommand(winmonFrame, commandWriteRAMViaID, 0x0d, 0x00, 0x01, 0x00)...)
|
|
expected = append(expected, buildSentCommand(winmonFrame, commandWriteRAMVar, 0x0d, 0x00)...)
|
|
expected = append(expected, buildSentCommand(winmonFrame, commandWriteData, 0x01, 0x00)...)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_WriteSettingByID(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
writeAck: make(chan byte, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandWriteViaIDResponse)
|
|
}()
|
|
|
|
err := m.WriteSettingByID(0x1234, 1234)
|
|
assert.NoError(t, err)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandWriteViaID, 0x34, 0x12, 0xd2, 0x04)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_WriteRAMVarByID(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
writeAck: make(chan byte, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandWriteRAMViaIDResponse)
|
|
}()
|
|
|
|
err := m.WriteRAMVarByID(0x000d, 1)
|
|
assert.NoError(t, err)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandWriteRAMViaID, 0x0d, 0x00, 0x01, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_GetDeviceState(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandSetStateResponse, []byte{0x00, byte(DeviceStateOn)})
|
|
}()
|
|
|
|
state, err := m.GetDeviceState()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, DeviceStateOn, state)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandSetState, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_SetDeviceState(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandSetStateResponse, []byte{0x00, byte(DeviceStateOff)})
|
|
}()
|
|
|
|
err := m.SetDeviceState(DeviceStateOff)
|
|
assert.NoError(t, err)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandSetState, 0x00, byte(DeviceStateOff))
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_ReadRAMVarByID(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandReadRAMResponse, []byte{0x34, 0x12})
|
|
}()
|
|
|
|
value, err := m.ReadRAMVarByID(0x0021)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int16(0x1234), value)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandReadRAMVar, 0x21, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_ReadSettingByID(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandReadSettingResponse, []byte{0xcd, 0xab})
|
|
}()
|
|
|
|
value, err := m.ReadSettingByID(0x0042)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int16(-21555), value)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandReadSetting, 0x42, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_ReadSelected(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandReadSelectedResponse, []byte{0x78, 0x56})
|
|
}()
|
|
|
|
value, err := m.ReadSelected()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int16(0x5678), value)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandReadSelected)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_SelectRAMVar(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandReadRAMResponse, []byte{0x11, 0x22})
|
|
}()
|
|
|
|
err := m.SelectRAMVar(0x0022)
|
|
assert.NoError(t, err)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandReadRAMVar, 0x22, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_SelectSetting(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandReadSettingResponse, []byte{0x11, 0x22})
|
|
}()
|
|
|
|
err := m.SelectSetting(0x0023)
|
|
assert.NoError(t, err)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandReadSetting, 0x23, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_ReadRAMVarInfo(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
winmonAck: make(chan winmonResponse, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWinmonResponse(commandGetRAMVarInfoResponse, []byte{0x9c, 0x7f, 0x00, 0x8f, 0x00})
|
|
}()
|
|
|
|
info, err := m.ReadRAMVarInfo(0x0001)
|
|
assert.NoError(t, err)
|
|
assert.True(t, info.Supported)
|
|
assert.Equal(t, uint16(0x0001), info.ID)
|
|
assert.Equal(t, int16(0x7f9c), info.Scale)
|
|
assert.Equal(t, int16(0x008f), info.Offset)
|
|
assert.InDelta(t, 0.01, info.Factor, testDelta)
|
|
|
|
expected := buildSentCommand(winmonFrame, commandGetRAMVarInfo, 0x01, 0x00)
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_WriteRAMVarRejected(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
writeAck: make(chan byte, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushWriteResponse(commandWriteNotAllowedResponse)
|
|
}()
|
|
|
|
err := m.WriteRAMVar(0x000d, 1)
|
|
assert.Error(t, err)
|
|
assert.ErrorContains(t, err, "rejected")
|
|
}
|
|
|
|
func Test_mk2Ser_SetPanelState(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
stateAck: make(chan struct{}, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushStateResponse()
|
|
}()
|
|
|
|
currentLimit := 16.5
|
|
err := m.SetPanelState(PanelSwitchOn, ¤tLimit)
|
|
assert.NoError(t, err)
|
|
|
|
expected := []byte{
|
|
0x07, 0xff, 0x53, 0x03, 0xa5, 0x00, 0x01, 0x80, 0x7e,
|
|
}
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_SetPanelState_SwitchOnly(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
stateAck: make(chan struct{}, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushStateResponse()
|
|
}()
|
|
|
|
err := m.SetPanelState(PanelSwitchOff, nil)
|
|
assert.NoError(t, err)
|
|
|
|
expected := []byte{
|
|
0x07, 0xff, 0x53, 0x04, 0x00, 0x80, 0x01, 0x80, 0xa2,
|
|
}
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_SetStandby(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
ifaceAck: make(chan byte, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushInterfaceResponse(interfacePanelDetectFlag | interfaceStandbyFlag)
|
|
}()
|
|
|
|
err := m.SetStandby(true)
|
|
assert.NoError(t, err)
|
|
|
|
expected := []byte{
|
|
0x03, 0xff, 0x48, 0x03, 0xb3,
|
|
}
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_mk2Ser_SetStandby_Disabled(t *testing.T) {
|
|
testIO := NewIOStub(nil)
|
|
m := &mk2Ser{
|
|
p: testIO,
|
|
ifaceAck: make(chan byte, 1),
|
|
}
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
m.pushInterfaceResponse(interfacePanelDetectFlag)
|
|
}()
|
|
|
|
err := m.SetStandby(false)
|
|
assert.NoError(t, err)
|
|
|
|
expected := []byte{
|
|
0x03, 0xff, 0x48, 0x01, 0xb5,
|
|
}
|
|
assert.Equal(t, expected, writeBuffer.Bytes())
|
|
}
|
|
|
|
func Test_parseFrameLength(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
raw byte
|
|
expectedLen byte
|
|
expectedFlag bool
|
|
}{
|
|
{
|
|
name: "normal length",
|
|
raw: 0x07,
|
|
expectedLen: 0x07,
|
|
expectedFlag: false,
|
|
},
|
|
{
|
|
name: "length with LED flag",
|
|
raw: 0x86,
|
|
expectedLen: 0x06,
|
|
expectedFlag: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
length, hasLEDStatus := parseFrameLength(tt.raw)
|
|
assert.Equal(t, tt.expectedLen, length)
|
|
assert.Equal(t, tt.expectedFlag, hasLEDStatus)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_mk2Ser_handleFrame_StateFrameWithAppendedLED(t *testing.T) {
|
|
m := &mk2Ser{
|
|
info: &Mk2Info{},
|
|
stateAck: make(chan struct{}, 1),
|
|
}
|
|
|
|
length, frame := buildTestFrame(frameHeader, stateFrame)
|
|
m.handleFrame(length, frame, []byte{0x03, 0x00})
|
|
|
|
select {
|
|
case <-m.stateAck:
|
|
default:
|
|
t.Fatal("expected state acknowledgement")
|
|
}
|
|
|
|
assert.NotNil(t, m.info.LEDs)
|
|
assert.Equal(t, LedOn, m.info.LEDs[LedMain])
|
|
assert.Equal(t, LedOn, m.info.LEDs[LedAbsorption])
|
|
assert.Equal(t, LedOff, m.info.LEDs[LedBulk])
|
|
}
|