Merge pull request #29 from diebietse/fix-signedness

No longer use RAM value scale signedness for info frames.
This commit is contained in:
Nicholas Thompson
2022-01-25 10:32:56 +02:00
committed by GitHub
2 changed files with 166 additions and 65 deletions

View File

@@ -49,7 +49,7 @@ const (
acL1InfoFrame = 0x08 acL1InfoFrame = 0x08
dcInfoFrame = 0x0C dcInfoFrame = 0x0C
setTargetFrame = 0x41 setTargetFrame = 0x41
infoReqFrame = 0x46 infoReqFrame = 0x46 //F
ledFrame = 0x4C ledFrame = 0x4C
vFrame = 0x56 vFrame = 0x56
winmonFrame = 0x57 winmonFrame = 0x57
@@ -358,10 +358,10 @@ func (m *mk2Ser) dcDecode(frame []byte) {
// Decodes AC frame. // Decodes AC frame.
func (m *mk2Ser) acDecode(frame []byte) { func (m *mk2Ser) acDecode(frame []byte) {
m.info.InVoltage = m.applyScaleAndSign(frame[5:7], ramVarVMains) m.info.InVoltage = m.applyScale(getSigned(frame[5:7]), ramVarVMains)
m.info.InCurrent = m.applyScaleAndSign(frame[7:9], ramVarIMains) m.info.InCurrent = m.applyScale(getSigned(frame[7:9]), ramVarIMains)
m.info.OutVoltage = m.applyScaleAndSign(frame[9:11], ramVarVInverter) m.info.OutVoltage = m.applyScale(getSigned(frame[9:11]), ramVarVInverter)
m.info.OutCurrent = m.applyScaleAndSign(frame[11:13], ramVarIInverter) m.info.OutCurrent = m.applyScale(getSigned(frame[11:13]), ramVarIInverter)
if frame[13] == 0xff { if frame[13] == 0xff {
m.info.InFrequency = 0 m.info.InFrequency = 0

View File

@@ -33,7 +33,7 @@ var knownWrites = []byte{
var writeBuffer = bytes.NewBuffer(nil) var writeBuffer = bytes.NewBuffer(nil)
const ( const (
testEpsilon = 0.00000001 testDelta = 0.00000001
) )
type testIo struct { type testIo struct {
@@ -50,62 +50,162 @@ func NewIOStub(readBuffer []byte) io.ReadWriter {
// Test a know sequence as reference as extracted from Mk2 // Test a know sequence as reference as extracted from Mk2
func TestSync(t *testing.T) { func TestSync(t *testing.T) {
knownReadBuffer := []byte{ tests := []struct {
//Len Cmd name string
0x04, 0xff, 0x41, 0x01, 0x00, 0xbb, 0x07, 0xff, 0x56, 0x96, 0x3e, 0x11, 0x00, 0x00, 0xbf, knownReadBuffer []byte
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a, knownWrites []byte
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1, result Mk2Info
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, name: "basic",
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1, knownReadBuffer: []byte{
0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a, //Len Cmd
0x08, 0xff, 0x57, 0x8e, 0x57, 0x78, 0x8f, 0x00, 0x01, 0xb5, 0x04, 0xff, 0x41, 0x01, 0x00, 0xbb,
0x08, 0xff, 0x57, 0x8e, 0x2f, 0x7c, 0x8f, 0x00, 0x00, 0xda, 0x07, 0xff, 0x56, 0x96, 0x3e, 0x11, 0x00, 0x00, 0xbf,
0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1, 0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
0x08, 0xff, 0x57, 0x8e, 0x04, 0x00, 0x8f, 0x00, 0x80, 0x01, 0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1,
0x08, 0xff, 0x57, 0x8e, 0x01, 0x00, 0x8f, 0x00, 0x80, 0x04, 0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
0x08, 0xff, 0x57, 0x8e, 0x02, 0x00, 0x8f, 0x00, 0x80, 0x03, 0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
0x08, 0xff, 0x57, 0x8e, 0x38, 0x7f, 0x8f, 0x00, 0x00, 0xce, 0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
0x07, 0xff, 0x56, 0x96, 0x3e, 0x11, 0x00, 0x00, 0xbf, 0x08, 0xff, 0x57, 0x8e, 0x64, 0x80, 0x8f, 0x00, 0x00, 0xa1,
0x0f, 0x20, 0xf3, 0x00, 0xc8, 0x02, 0x0c, 0xa1, 0x05, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x88, 0xb2, 0x08, 0xff, 0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a,
0x0f, 0x20, 0x01, 0x01, 0xca, 0x09, 0x08, 0xaa, 0x58, 0xab, 0x00, 0xaa, 0x58, 0x9a, 0x00, 0xc3, 0xe8, 0x08, 0xff, 0x57, 0x8e, 0x57, 0x78, 0x8f, 0x00, 0x01, 0xb5,
0x06, 0xff, 0x4c, 0x03, 0x00, 0x00, 0x00, 0xac, 0x08, 0xff, 0x57, 0x8e, 0x2f, 0x7c, 0x8f, 0x00, 0x00, 0xda,
0x05, 0xff, 0x57, 0x85, 0xc8, 0x00, 0x58, 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,
},
},
},
} }
expectedLEDs := map[Led]LEDstate{ for _, tt := range tests {
LedMain: LedOn, t.Run(tt.name, func(t *testing.T) {
LedAbsorption: LedOn, testIO := NewIOStub(tt.knownReadBuffer)
LedBulk: LedOff, mk2, err := NewMk2Connection(testIO)
LedFloat: LedOff, assert.NoError(t, err, "Could not open MK2")
LedInverter: LedOff,
LedOverload: LedOff, event := <-mk2.C()
LedLowBattery: LedOff, mk2.Close()
LedTemperature: LedOff,
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")
})
} }
testIO := NewIOStub(knownReadBuffer)
mk2, err := NewMk2Connection(testIO)
assert.NoError(t, err, "Could not open MK2")
event := <-mk2.C()
mk2.Close()
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, uint32(2736), event.Version, "Invalid version decoded")
assert.Equal(t, 0, len(event.Errors), "Reported errors not empty")
assert.Equal(t, expectedLEDs, event.LEDs, "Reported LEDs incorrect")
assert.InEpsilon(t, 14.41, event.BatVoltage, testEpsilon, "BatVoltage conversion failed")
assert.InEpsilon(t, -0.4, event.BatCurrent, testEpsilon, "BatCurrent conversion failed")
assert.InEpsilon(t, 226.98, event.InVoltage, testEpsilon, "InVoltage conversion failed")
assert.InEpsilon(t, 1.71, event.InCurrent, testEpsilon, "InCurrent conversion failed")
assert.InEpsilon(t, 50.10256410256411, event.InFrequency, testEpsilon, "InFrequency conversion failed")
assert.InEpsilon(t, 226.980, event.OutVoltage, testEpsilon, "OutVoltage conversion failed")
assert.InEpsilon(t, 1.54, event.OutCurrent, testEpsilon, "OutCurrent conversion failed")
assert.InEpsilon(t, 50.025510204081634, event.OutFrequency, testEpsilon, "OutFrequency conversion failed")
assert.InEpsilon(t, 1, event.ChargeState, testEpsilon, "ChargeState conversion failed")
} }
func Test_mk2Ser_scaleDecode(t *testing.T) { func Test_mk2Ser_scaleDecode(t *testing.T) {
@@ -116,16 +216,16 @@ func Test_mk2Ser_scaleDecode(t *testing.T) {
}{ }{
{ {
name: "Valid scale", name: "Valid scale",
frame: []byte{0x57, 0x8e, 0x9c, 0x7f, 0x8f, 0x00, 0x00, 0x6a}, frame: []byte{0x8e, 0x9c, 0x7f, 0x8f, 0x01, 0x00, 0x6a},
expectedScaling: scaling{ expectedScaling: scaling{
scale: 0.00013679890560875513, scale: 0.01,
offset: 143, offset: 1,
supported: true, supported: true,
}, },
}, },
{ {
name: "Unsupported frame", name: "Unsupported frame",
frame: []byte{0x57, 0x00}, frame: []byte{0x00},
expectedScaling: scaling{ expectedScaling: scaling{
supported: false, supported: false,
}, },
@@ -141,9 +241,10 @@ func Test_mk2Ser_scaleDecode(t *testing.T) {
assert.Equal(t, 1, len(m.scales)) assert.Equal(t, 1, len(m.scales))
assert.Equal(t, 1, m.scaleCount) assert.Equal(t, 1, m.scaleCount)
assert.Equal(t, tt.expectedScaling.supported, m.scales[0].supported) assert.Equal(t, tt.expectedScaling.supported, m.scales[0].supported)
assert.Equal(t, tt.expectedScaling.signed, m.scales[0].signed)
if tt.expectedScaling.supported { if tt.expectedScaling.supported {
assert.InEpsilon(t, tt.expectedScaling.offset, m.scales[0].offset, testEpsilon) assert.InDelta(t, tt.expectedScaling.offset, m.scales[0].offset, testDelta)
assert.InEpsilon(t, tt.expectedScaling.scale, m.scales[0].scale, testEpsilon) assert.InDelta(t, tt.expectedScaling.scale, m.scales[0].scale, testDelta)
} }
}) })
} }