diff --git a/src-go/arke/pd_config.go b/src-go/arke/pd_config.go index e219fed..70a146d 100644 --- a/src-go/arke/pd_config.go +++ b/src-go/arke/pd_config.go @@ -1,24 +1,34 @@ package arke +import "fmt" + type PDConfig struct { DeadRegion uint8 ProportionnalMultiplier uint8 DerivativeMultiplier uint8 ProportionalDivider uint8 DerivativeDivider uint8 } -func (c PDConfig) marshall(buffer []byte) { +func (c PDConfig) marshall(buffer []byte) error { + if c.ProportionalDivider > 15 { + return fmt.Errorf("Maximal Proportional Divider is 15") + } + if c.DerivativeDivider > 15 { + return fmt.Errorf("Maximal Derivative Divider is 15") + } + buffer[0] = c.DeadRegion buffer[1] = c.ProportionnalMultiplier buffer[2] = c.DerivativeMultiplier - buffer[3] = ((c.ProportionalDivider & 0x0f) << 4) | c.DerivativeDivider&0x0f + buffer[3] = ((c.DerivativeDivider & 0x0f) << 4) | c.ProportionalDivider&0x0f + return nil } func (c PDConfig) unmarshall(buffer []byte) { c.DeadRegion = buffer[0] c.ProportionnalMultiplier = buffer[1] c.DerivativeMultiplier = buffer[2] - c.DerivativeDivider = buffer[3] & 0x0f - c.ProportionalDivider = (buffer[3] & 0xf0) >> 4 + c.ProportionalDivider = buffer[3] & 0x0f + c.DerivativeDivider = (buffer[3] & 0xf0) >> 4 } diff --git a/src-go/arke/zeus.go b/src-go/arke/zeus.go index 2768b01..6cf3a88 100644 --- a/src-go/arke/zeus.go +++ b/src-go/arke/zeus.go @@ -1,159 +1,163 @@ package arke import ( "encoding/binary" "fmt" "math" ) // #include "../../include/arke.h" import "C" type ZeusSetPoint struct { Humidity float32 Temperature float32 Wind uint8 } func checkSize(buf []byte, expected int) error { if len(buf) < expected { return fmt.Errorf("Invalid buffer size %d, required %d", len(buf), expected) } return nil } func (m ZeusSetPoint) Marshall(buf []byte) (int, error) { if err := checkSize(buf, 5); err != nil { return 0, err } binary.LittleEndian.PutUint16(buf[0:], humidityFloatToBinary(m.Humidity)) binary.LittleEndian.PutUint16(buf[2:], hih6030TemperatureFloatToBinary(m.Temperature)) buf[4] = m.Wind return 5, nil } func (m *ZeusSetPoint) Unmarshall(buf []byte) error { if err := checkSize(buf, 5); err != nil { return err } m.Humidity = humidityBinaryToFloat(binary.LittleEndian.Uint16(buf[0:])) if math.IsNaN(float64(m.Humidity)) == true { return fmt.Errorf("Invalid humidity value") } m.Temperature = hih6030TemperatureBinaryToFloat(binary.LittleEndian.Uint16(buf[2:])) if math.IsNaN(float64(m.Temperature)) == true { return fmt.Errorf("Invalid temperature value") } m.Wind = buf[4] return nil } type ZeusReport struct { Humidity float32 Temperature [4]float32 } func (m *ZeusReport) Unmarshall(buf []byte) error { if err := checkSize(buf, 8); err != nil { return err } packed := []uint16{ binary.LittleEndian.Uint16(buf[0:]), binary.LittleEndian.Uint16(buf[2:]), binary.LittleEndian.Uint16(buf[4:]), binary.LittleEndian.Uint16(buf[6:]), } m.Humidity = humidityBinaryToFloat(packed[0] & 0x3fff) if math.IsNaN(float64(m.Humidity)) == true { return fmt.Errorf("Invalid humidity value") } m.Temperature[0] = hih6030TemperatureBinaryToFloat((packed[0] >> 14) | (packed[1]&0x0fff)<<2) if math.IsNaN(float64(m.Temperature[0])) == true { return fmt.Errorf("Invalid Temperature[0] value") } m.Temperature[1] = tmp1075BinaryToFloat((packed[1] >> 12) | (packed[2]&0x00ff)<<4) m.Temperature[2] = tmp1075BinaryToFloat((packed[2] >> 8) | (packed[3]&0x000f)<<8) m.Temperature[3] = tmp1075BinaryToFloat((packed[3] & 0xfff0) >> 4) return nil } type ZeusConfig struct { Humidity PDConfig Temperature PDConfig } func (m ZeusConfig) Marshall(buf []byte) (int, error) { if len(buf) < 8 { return 0, fmt.Errorf("Invalid buffer size %d, required 8", len(buf)) } - m.Humidity.marshall(buf[0:]) - m.Temperature.marshall(buf[4:]) + if err := m.Humidity.marshall(buf[0:]); err != nil { + return 0, err + } + if err := m.Temperature.marshall(buf[4:]); err != nil { + return 4, err + } return 8, nil } func (m *ZeusConfig) Unmarshall(buf []byte) error { m.Humidity.unmarshall(buf[0:]) m.Temperature.unmarshall(buf[4:]) return nil } type ZeusStatus struct { status uint8 fans [2]FanStatus humidity int16 temperature int16 } type ZeusControlStatus uint8 const ( ZeusIdle ZeusControlStatus = C.ARKE_ZEUS_IDLE ZeusActive ZeusControlStatus = C.ARKE_ZEUS_ACTIVE ZeusClimateNotControlledWatchDog ZeusControlStatus = C.ARKE_ZEUS_CLIMATE_UNCONTROLLED_WD ) func (s ZeusStatus) ControlStatus() ZeusControlStatus { return ZeusControlStatus(s.status & 0x03) } func (s ZeusStatus) IsFanStatus() bool { return (s.status)&C.ARKE_ZEUS_STATUS_IS_COMMAND_DATA == 0 } func (s ZeusStatus) IsCommandData() bool { return (s.status)&C.ARKE_ZEUS_STATUS_IS_COMMAND_DATA == 0 } func (s ZeusStatus) FanStatus() ([2]FanStatus, error) { if s.IsCommandData() { return [2]FanStatus{}, fmt.Errorf("Packet contains command data") } return s.fans, nil } func (s ZeusStatus) HumidityCommand() (int16, error) { if s.IsFanStatus() { return 0, fmt.Errorf("Packet contains fan status") } return s.humidity, nil } func (s ZeusStatus) TemperatureCommand() (int16, error) { if s.IsFanStatus() { return 0, fmt.Errorf("Packet contains fan status") } return s.temperature, nil } func (m ZeusStatus) Unmarshall(buf []byte) error { m.status = buf[0] if m.IsFanStatus() { m.fans[0] = FanStatus(binary.LittleEndian.Uint16(buf[1:])) m.fans[1] = FanStatus(binary.LittleEndian.Uint16(buf[3:])) } else { m.humidity = int16(binary.LittleEndian.Uint16(buf[1:])) m.temperature = int16(binary.LittleEndian.Uint16(buf[3:])) } return nil } diff --git a/src-go/arke/zeus_test.go b/src-go/arke/zeus_test.go index 004436d..65c6138 100644 --- a/src-go/arke/zeus_test.go +++ b/src-go/arke/zeus_test.go @@ -1,135 +1,137 @@ package arke import ( "fmt" "math" . "gopkg.in/check.v1" ) type ZeusSuite struct{} var _ = Suite(&ZeusSuite{}) type almostEqualChecker struct { *CheckerInfo } var AlmostChecker = &almostEqualChecker{ &CheckerInfo{Name: "AlmostEqual", Params: []string{"obtained", "expected", "within"}}, } func (c *almostEqualChecker) Check(params []interface{}, names []string) (result bool, error string) { defer func() { if v := recover(); v != nil { result = false error = fmt.Sprint(v) } }() a := params[0].(float32) b := params[1].(float32) bound := params[2].(float64) result = math.Abs(float64(a-b)) <= bound return } func (s *ZeusSuite) TestSetPointIO(c *C) { testData := []struct { Message ZeusSetPoint Buffer []byte }{ { Message: ZeusSetPoint{ Humidity: 42, Temperature: 25, Wind: 127, }, Buffer: []byte{ 0xe0, 0x1a, 0x35, 0x19, 0x7f, }, }, } for _, d := range testData { res := make([]byte, 5) written, err := d.Message.Marshall(res) if c.Check(err, IsNil) == false { continue } c.Check(written, Equals, 5) c.Check(res, DeepEquals, d.Buffer) } for _, d := range testData { res := ZeusSetPoint{} if c.Check(res.Unmarshall(d.Buffer), IsNil) == false { continue } c.Check(res.Wind, Equals, d.Message.Wind) c.Check(res.Humidity, AlmostChecker, d.Message.Humidity, 0.01) c.Check(res.Temperature, AlmostChecker, d.Message.Temperature, 0.01) } m := ZeusSetPoint{} written, err := m.Marshall([]byte{}) c.Check(err, ErrorMatches, "Invalid buffer size .*") c.Check(written, Equals, 0) c.Check(m.Unmarshall([]byte{}), ErrorMatches, "Invalid buffer size .*") errorData := []struct { Buffer []byte EMatch string }{ { []byte{ 0xff, 0xff, 0x00, 0x00, 0x00, }, "Invalid humidity value", }, { []byte{ 0x00, 0x00, 0xff, 0xff, 0x00, }, "Invalid temperature value", }, } for _, d := range errorData { m := ZeusSetPoint{} c.Check(m.Unmarshall(d.Buffer), ErrorMatches, d.EMatch) } } func (s *ZeusSuite) TestReportIO(c *C) { testData := []struct { Message ZeusReport Buffer []byte }{ { Message: ZeusReport{ Humidity: 40, Temperature: [4]float32{ 25, 26, 27, 28, }, }, Buffer: []byte{ 0x99, 0x99, 0x4d, 0x06, 0x1a, 0xb0, 0x01, 0x1c, }, }, } for _, d := range testData { res := ZeusReport{} if c.Check(res.Unmarshall(d.Buffer), IsNil) == false { continue } c.Check(res.Humidity, AlmostChecker, d.Message.Humidity, 0.01) for i, _ := range res.Temperature { c.Check(res.Temperature[i], AlmostChecker, d.Message.Temperature[i], 0.01) } } + m := ZeusReport{} + c.Check(m.Unmarshall([]byte{}), ErrorMatches, "Invalid buffer size .*") }