diff --git a/include/arke.h b/include/arke.h index 8726040..087357a 100644 --- a/include/arke.h +++ b/include/arke.h @@ -1,155 +1,154 @@ #pragma once #include "inttypes.h" #ifdef __cplusplus extern "C"{ #endif //__cplusplus typedef enum ArkeMessageType_e { ARKE_NETWORK_CONTROL_COMMAND = 0x00, ARKE_HIGH_PRIORITY_MESSAGE = 0x01, ARKE_MESSAGE = 0x02, ARKE_HEARTBEAT = 0x03, ARKE_MESSAGE_TYPE_MASK = 0x03 << 9 } ArkeMessageType; typedef enum ArkeNodeClass_e { ARKE_BROADCAST = 0x0, ARKE_ZEUS = 0x38, ARKE_HELIOS = 0x34, ARKE_CELAENO = 0x30, ARKE_NODE_CLASS_MASK = 0x3f << 3 } ArkeNodeClass; typedef enum ArkeNetworkCommand_e { ARKE_RESET_REQUEST = 0x00, ARKE_SYNCHRONISATION = 0x01, ARKE_HEARTBEAT_REQUEST = 0x07, ARKE_SUBID_MASK = 0x07 } ArkeNetworkCommand; //#define ARKE_SUBID_MASK ARKE_HEARTBEAT_REQUEST typedef enum ArkeMessageClass_e { ARKE_ZEUS_SET_POINT = 0x38, ARKE_ZEUS_REPORT = 0x39, ARKE_ZEUS_VIBRATION_REPORT = 0x3a, ARKE_ZEUS_CONFIG = 0x3b, ARKE_ZEUS_STATUS = 0x3c, ARKE_ZEUS_CONTROL_POINT = 0x3d, ARKE_HELIOS_SET_POINT = 0x34, ARKE_HELIOS_PULSE_MODE = 0x35, ARKE_CELAENO_SET_POINT = 0x30, ARKE_CELAENO_STATUS = 0x31, ARKE_CELAENO_CONFIG = 0x32 } ArkeMessageClass; struct ArkeZeusSetPoint_t { uint16_t Humidity; uint16_t Temperature; uint8_t Wind; } __attribute__((packed)); typedef struct ArkeZeusSetPoint_t ArkeZeusSetPoint; struct ArkeZeusReport_t { uint16_t Humidity:14; uint16_t Temperature1:14; uint16_t Temperature2:12; uint16_t Temperature3:12; uint16_t Temperature4:12; } __attribute__((packed)); typedef struct ArkeZeusReport_t ArkeZeusReport; struct ArkePDConfig_t { - uint8_t DeadRegion; uint8_t ProportionalMult; uint8_t DerivativeMult; - uint8_t ProportionalDivPower2:4; - uint8_t DerivativeDivPower2:4; + uint8_t IntegralMult; + uint8_t DividerPower; } __attribute__((packed)); typedef struct ArkePDConfig_t ArkePDConfig; struct ArkeZeusConfig_t { ArkePDConfig Humidity; ArkePDConfig Temperature; } __attribute__((packed)); typedef struct ArkeZeusConfig_t ArkeZeusConfig; #define ARKE_FAN_AGING_ALERT (0x4000) #define ARKE_FAN_STALL_ALERT (0x8000) #define ARKE_FAN_RPM_MASK (0x3fff) #define ArkeFanAging(status) (((status).fanStatus & ARKE_FAN_AGING_ALERT) != 0x0000) #define ArkeFanStall(status) (((status).fanStatus & ARKE_FAN_STALL_ALERT) != 0x0000) #define ArkeFanRPM(status) ( (status).fanStatus & ARKE_FAN_RPM_MASK ) typedef uint16_t ArkeFanStatus; struct ArkeZeusStatus_t { uint8_t Status; ArkeFanStatus Fan[2]; } __attribute__((packed)); typedef struct ArkeZeusStatus_t ArkeZeusStatus; #define ARKE_ZEUS_IDLE 0x00 #define ARKE_ZEUS_ACTIVE 0x01 #define ARKE_ZEUS_CLIMATE_UNCONTROLLED_WD 0x02 struct ArkeZeusControlPoint_t { int16_t Humidity; int16_t Temperature; } __attribute__((packed)); typedef struct ArkeZeusControlPoint_t ArkeZeusControlPoint; struct ArkeHeliosSetPoint_t { uint8_t Visible; uint8_t UV; } __attribute__((packed)); typedef struct ArkeHeliosSetPoint_t ArkeHeliosSetPoint; struct ArkeCelaenoSetPoint_t { uint8_t Power; } __attribute__((packed)); typedef struct ArkeCelaenoSetPoint_t ArkeCelaenoSetPoint; struct ArkeCelaenoStatus_t { uint8_t waterLevel; ArkeFanStatus fanStatus; } __attribute__((packed)); typedef struct ArkeCelaenoStatus_t ArkeCelaenoStatus; typedef enum ArkeCelaenoWaterLevel_e { ARKE_CELAENO_NOMINAL = 0, ARKE_CELAENO_WARNING = (1 << 0), ARKE_CELAENO_CRITICAL = (1 << 1), ARKE_CELAENO_RO_ERROR = (1 << 2) } ArkeCelaenoWaterLevel; #define ArkeCelaenoWaterNominal(status) ( (status).waterLevel == 0 ) #define ArkeCelaenoWaterWarning(status) ( (status).waterLevel == ARKE_CELAENO_WARNING ) #define ArkeCelaenoWaterCritical(status) ( ((status).waterLevel & ~(ARKE_CELAENO_CRITICAL | ARKE_CELAENO_RO_ERROR) ) == ARKE_CELAENO_WARNING ) #define ArkeCelaenoWaterHasRoError(status) (((status).waterLevel & ARKE_CELAENO_RO_ERROR) != 0x00) struct ArkeCelaenoConfig_t { uint16_t RampUpTimeMS; uint16_t RampDownTimeMS; uint16_t MinOnTimeMS; uint16_t DebounceTimeMS; } __attribute__((packed)); typedef struct ArkeCelaenoConfig_t ArkeCelaenoConfig; #ifdef __cplusplus } #endif //__cplusplus diff --git a/src-go/arke/pd_config.go b/src-go/arke/pd_config.go index 856065b..a3aa7f0 100644 --- a/src-go/arke/pd_config.go +++ b/src-go/arke/pd_config.go @@ -1,34 +1,23 @@ package arke -import "fmt" - type PDConfig struct { - DeadRegion uint8 ProportionnalMultiplier uint8 DerivativeMultiplier uint8 - ProportionalDivider uint8 - DerivativeDivider uint8 + IntegralMultiplier uint8 + DividerPower uint8 } 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.DerivativeDivider & 0x0f) << 4) | c.ProportionalDivider&0x0f + buffer[0] = c.ProportionnalMultiplier + buffer[1] = c.DerivativeMultiplier + buffer[2] = c.IntegralMultiplier + buffer[3] = c.DividerPower return nil } func (c *PDConfig) unmarshall(buffer []byte) { - c.DeadRegion = buffer[0] - c.ProportionnalMultiplier = buffer[1] - c.DerivativeMultiplier = buffer[2] - c.ProportionalDivider = buffer[3] & 0x0f - c.DerivativeDivider = (buffer[3] & 0xf0) >> 4 + c.ProportionnalMultiplier = buffer[0] + c.DerivativeMultiplier = buffer[1] + c.IntegralMultiplier = buf[2] + c.DividerPower = buf[2] } diff --git a/src-go/arke/zeus_test.go b/src-go/arke/zeus_test.go index f5630c5..af49415 100644 --- a/src-go/arke/zeus_test.go +++ b/src-go/arke/zeus_test.go @@ -1,330 +1,310 @@ 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) } } errorData := []struct { Buffer []byte Ematch string }{ { []byte{}, "Invalid buffer size .*", }, { []byte{0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Invalid humidity value", }, { []byte{0x00, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, "Invalid temperature value", }, } for _, d := range errorData { m := ZeusReport{} c.Check(m.Unmarshall(d.Buffer), ErrorMatches, d.Ematch) } } func (s *ZeusSuite) TestConfigIO(c *C) { testData := []struct { Message ZeusConfig Buffer []byte }{ { Message: ZeusConfig{ Humidity: PDConfig{ - DeadRegion: 127, ProportionnalMultiplier: 100, DerivativeMultiplier: 50, - ProportionalDivider: 1, - DerivativeDivider: 2, + IntegralMultiplier: 1, + DividerPower: 6, }, Temperature: PDConfig{ - DeadRegion: 60, ProportionnalMultiplier: 103, DerivativeMultiplier: 102, - ProportionalDivider: 4, - DerivativeDivider: 5, + IntegralMultiplier: 0, + DividerPower: 4, }, }, Buffer: []byte{ - 127, 100, 50, (2 << 4) | 1, 60, 103, 102, (5 << 4) | 4, + 100, 50, 1, 6, 103, 102, 0, 4, }, }, } for _, d := range testData { res := ZeusConfig{} if c.Check(res.Unmarshall(d.Buffer), IsNil) == false { continue } c.Check(res, DeepEquals, d.Message) } for _, d := range testData { res := make([]byte, 8) n, err := d.Message.Marshall(res) if c.Check(err, IsNil) == false { continue } c.Check(n, Equals, 8) c.Check(res, DeepEquals, d.Buffer) } errorData := []struct { Message ZeusConfig Buffer []byte Ematch string }{ { ZeusConfig{}, make([]byte, 0), "Invalid buffer size .*", }, - { - ZeusConfig{ - Humidity: PDConfig{ - DerivativeDivider: 16, - }, - }, - make([]byte, 8), - "Maximal Derivative Divider is 15", - }, - { - ZeusConfig{ - Temperature: PDConfig{ - ProportionalDivider: 16, - }, - }, - make([]byte, 8), - "Maximal Proportional Divider is 15", - }, } for _, d := range errorData { _, err := d.Message.Marshall(d.Buffer) c.Check(err, ErrorMatches, d.Ematch) } m := ZeusConfig{} c.Check(m.Unmarshall([]byte{}), ErrorMatches, "Invalid buffer size .*") } func (s *ZeusSuite) TestStatusIO(c *C) { testData := []struct { Message ZeusStatus Buffer []byte }{ { Message: ZeusStatus{ Status: ZeusIdle, Fans: [2]FanStatusAndRPM{ 1200, FanStatusAndRPM(0 | (uint16(FanStalled) << 14)), }, }, Buffer: []byte{ 0, 0xb0, 0x04, 0x00, 0x80, }, }, } for _, d := range testData { res := ZeusStatus{} if c.Check(res.Unmarshall(d.Buffer), IsNil) == false { continue } c.Check(res, DeepEquals, d.Message) } errorData := []struct { Buffer []byte Ematch string }{ { make([]byte, 0), "Invalid buffer size .*", }, } for _, d := range errorData { m := ZeusStatus{} err := m.Unmarshall(d.Buffer) c.Check(err, ErrorMatches, d.Ematch) } } func (s *ZeusSuite) TestControlPointIO(c *C) { testData := []struct { Message ZeusControlPoint Buffer []byte }{ { Message: ZeusControlPoint{ Humidity: 1234, Temperature: -275, }, Buffer: []byte{ 0xd2, 0x04, 0xed, 0xfe, }, }, } for _, d := range testData { res := ZeusControlPoint{} if c.Check(res.Unmarshall(d.Buffer), IsNil) == false { continue } c.Check(res, DeepEquals, d.Message) } errorData := []struct { Buffer []byte Ematch string }{ { make([]byte, 0), "Invalid buffer size .*", }, } for _, d := range errorData { m := ZeusControlPoint{} err := m.Unmarshall(d.Buffer) c.Check(err, ErrorMatches, d.Ematch) } }