diff --git a/cmd/http_test.go b/cmd/http_test.go index bb9d18e5..a21052cd 100644 --- a/cmd/http_test.go +++ b/cmd/http_test.go @@ -121,7 +121,7 @@ func TestSetHeaders(t *testing.T) { func TestHTTPCmd(t *testing.T) { go func() { // call the command handler function - httpCmd.Run(nil, []string{}) + httpCmd.Run(nil, []string{"--host", "::1"}) }() // create a new HTTP request to simulate the command execution diff --git a/pkg/decoder/decoder.go b/pkg/decoder/decoder.go index f4727cd4..73699bab 100644 --- a/pkg/decoder/decoder.go +++ b/pkg/decoder/decoder.go @@ -14,8 +14,9 @@ type FieldConfig struct { // PayloadConfig defines the overall structure of the payload, including the target struct type type PayloadConfig struct { - Fields []FieldConfig - TargetType reflect.Type + Fields []FieldConfig + TargetType reflect.Type + StatusByteIndex *int // can be nil } type Decoder interface { diff --git a/pkg/decoder/helpers/helpers.go b/pkg/decoder/helpers/helpers.go index 26a45fe6..d2da8848 100644 --- a/pkg/decoder/helpers/helpers.go +++ b/pkg/decoder/helpers/helpers.go @@ -151,3 +151,7 @@ func UintToBinaryArray(value uint64, length int) []byte { } return binaryArray } + +func ToIntPointer(value int) *int { + return &value +} diff --git a/pkg/decoder/tagsl/v1/decoder.go b/pkg/decoder/tagsl/v1/decoder.go index e89c3483..ba43e044 100644 --- a/pkg/decoder/tagsl/v1/decoder.go +++ b/pkg/decoder/tagsl/v1/decoder.go @@ -1,7 +1,6 @@ package tagsl import ( - "encoding/hex" "fmt" "reflect" @@ -39,7 +38,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Minute", Start: 15, Length: 1}, {Name: "Second", Start: 16, Length: 1}, }, - TargetType: reflect.TypeOf(Port1Payload{}), + TargetType: reflect.TypeOf(Port1Payload{}), + StatusByteIndex: helpers.ToIntPointer(0), }, nil case 2: return decoder.PayloadConfig{ @@ -107,7 +107,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Mac7", Start: 43, Length: 6, Optional: true, Hex: true}, {Name: "Rssi7", Start: 49, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port5Payload{}), + TargetType: reflect.TypeOf(Port5Payload{}), + StatusByteIndex: helpers.ToIntPointer(0), }, nil case 6: return decoder.PayloadConfig{ @@ -134,7 +135,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Mac6", Start: 40, Length: 6, Optional: true, Hex: true}, {Name: "Rssi6", Start: 46, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port7Payload{}), + TargetType: reflect.TypeOf(Port7Payload{}), + StatusByteIndex: helpers.ToIntPointer(4), }, nil case 10: return decoder.PayloadConfig{ @@ -157,7 +159,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "PDOP", Start: 18, Length: 1, Optional: true}, {Name: "Satellites", Start: 19, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port10Payload{}), + TargetType: reflect.TypeOf(Port10Payload{}), + StatusByteIndex: helpers.ToIntPointer(0), }, nil case 15: return decoder.PayloadConfig{ @@ -167,7 +170,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { return float64(v.(int)) / 1000 }}, }, - TargetType: reflect.TypeOf(Port15Payload{}), + TargetType: reflect.TypeOf(Port15Payload{}), + StatusByteIndex: helpers.ToIntPointer(0), }, nil case 50: return decoder.PayloadConfig{ @@ -200,7 +204,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Mac6", Start: 53, Length: 6, Optional: true, Hex: true}, {Name: "Rssi6", Start: 59, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port50Payload{}), + TargetType: reflect.TypeOf(Port50Payload{}), + StatusByteIndex: helpers.ToIntPointer(0), }, nil case 51: return decoder.PayloadConfig{ @@ -235,7 +240,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Mac6", Start: 55, Length: 6, Optional: true, Hex: true}, {Name: "Rssi6", Start: 61, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port51Payload{}), + TargetType: reflect.TypeOf(Port51Payload{}), + StatusByteIndex: helpers.ToIntPointer(0), }, nil case 105: return decoder.PayloadConfig{ @@ -256,7 +262,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Mac6", Start: 42, Length: 6, Optional: true, Hex: true}, {Name: "Rssi6", Start: 48, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port105Payload{}), + TargetType: reflect.TypeOf(Port105Payload{}), + StatusByteIndex: helpers.ToIntPointer(6), }, nil case 110: return decoder.PayloadConfig{ @@ -277,7 +284,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { return float64(v.(int)) / 1000 }}, }, - TargetType: reflect.TypeOf(Port110Payload{}), + TargetType: reflect.TypeOf(Port110Payload{}), + StatusByteIndex: helpers.ToIntPointer(2), }, nil case 150: return decoder.PayloadConfig{ @@ -311,7 +319,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Mac6", Start: 55, Length: 6, Optional: true, Hex: true}, {Name: "Rssi6", Start: 61, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port150Payload{}), + TargetType: reflect.TypeOf(Port150Payload{}), + StatusByteIndex: helpers.ToIntPointer(2), }, nil case 151: return decoder.PayloadConfig{ @@ -347,7 +356,8 @@ func (t TagSLv1Decoder) getConfig(port int16) (decoder.PayloadConfig, error) { {Name: "Mac6", Start: 57, Length: 6, Optional: true, Hex: true}, {Name: "Rssi6", Start: 63, Length: 1, Optional: true}, }, - TargetType: reflect.TypeOf(Port151Payload{}), + TargetType: reflect.TypeOf(Port151Payload{}), + StatusByteIndex: helpers.ToIntPointer(2), }, nil } @@ -365,7 +375,12 @@ func (t TagSLv1Decoder) Decode(data string, port int16, devEui string) (interfac return nil, nil, err } - statusData, err := parseStatusByte(data) + // if there is no status byte index, return the decoded data and nil for status data + if config.StatusByteIndex == nil { + return decodedData, nil, nil + } + + statusData, err := parseStatusByte(data[*config.StatusByteIndex]) if err != nil { return nil, nil, err } @@ -380,19 +395,7 @@ type Status struct { Moving bool `json:"moving"` } -func parseStatusByte(hexInput string) (Status, error) { - // Decode the hex string to bytes - data, err := hex.DecodeString(hexInput) - if err != nil { - return Status{}, err - } - - if len(data) == 0 { - return Status{}, fmt.Errorf("invalid input, no data found") - } - - statusByte := data[0] // Get the first byte - +func parseStatusByte(statusByte byte) (Status, error) { // Extract bits as per the requirements dcFlag := (statusByte >> 7) & 0x01 // Bit 7 confChangeID := (statusByte >> 3) & 0x0F // Bits 6:3 (4-bit) diff --git a/pkg/decoder/tagsl/v1/decoder_test.go b/pkg/decoder/tagsl/v1/decoder_test.go index 8bb09f57..2cd196ea 100644 --- a/pkg/decoder/tagsl/v1/decoder_test.go +++ b/pkg/decoder/tagsl/v1/decoder_test.go @@ -527,12 +527,12 @@ func TestInvalidPort(t *testing.T) { func TestParseStatusByte(t *testing.T) { tests := []struct { - hexInput string + input byte expected Status err error }{ { - hexInput: "80", + input: 0x80, expected: Status{ DutyCycle: true, ConfigChangeId: 0, @@ -542,7 +542,7 @@ func TestParseStatusByte(t *testing.T) { err: nil, }, { - hexInput: "FF", + input: 0xFF, expected: Status{ DutyCycle: true, ConfigChangeId: 15, @@ -552,7 +552,7 @@ func TestParseStatusByte(t *testing.T) { err: nil, }, { - hexInput: "00", + input: 0x00, expected: Status{ DutyCycle: false, ConfigChangeId: 0, @@ -562,7 +562,7 @@ func TestParseStatusByte(t *testing.T) { err: nil, }, { - hexInput: "4A", + input: 0x4A, expected: Status{ DutyCycle: false, ConfigChangeId: 9, @@ -572,7 +572,7 @@ func TestParseStatusByte(t *testing.T) { err: nil, }, { - hexInput: "8D", + input: 0x8D, expected: Status{ DutyCycle: true, ConfigChangeId: 1, @@ -581,21 +581,11 @@ func TestParseStatusByte(t *testing.T) { }, err: nil, }, - { - hexInput: "", - expected: Status{}, - err: fmt.Errorf("invalid input, no data found"), - }, - { - hexInput: "ZZ", - expected: Status{}, - err: fmt.Errorf("encoding/hex: invalid byte: U+005A 'Z'"), - }, } for _, test := range tests { - t.Run(fmt.Sprintf("TestHexInput%s", test.hexInput), func(t *testing.T) { - got, err := parseStatusByte(test.hexInput) + t.Run(fmt.Sprintf("TestStatusByteInput%v", test.input), func(t *testing.T) { + got, err := parseStatusByte(test.input) if err != nil && test.err == nil { t.Fatalf("unexpected error: %v", err) } @@ -611,3 +601,108 @@ func TestParseStatusByte(t *testing.T) { }) } } + +func TestFullDecode(t *testing.T) { + tests := []struct { + payload string + expectedData interface{} + expectedStatus *Status + port int16 + }{ + { + payload: "8002cdcd1300744f5e166018040b14341a", + expectedData: Port1Payload{ + Moving: false, + Latitude: 47.041811, + Longitude: 7.622494, + Altitude: 572.8, + Year: 24, + Month: 4, + Day: 11, + Hour: 20, + Minute: 52, + Second: 26, + }, + expectedStatus: &Status{ + DutyCycle: true, + ConfigChangeId: 0, + ConfigChangeSuccess: false, + Moving: false, + }, + port: 1, + }, + { + payload: "66ec04bb00e0286d8aabfcbbec6c9a74b58fb2726c9a74b58db1e0286d8a9478cbf0b0140c96bbd2260122180d42ad", + expectedData: Port7Payload{ + Timestamp: time.Date(2024, 9, 19, 11, 2, 19, 0, time.UTC), + Moving: false, + Mac1: "e0286d8aabfc", + Rssi1: -69, + Mac2: "ec6c9a74b58f", + Rssi2: -78, + Mac3: "726c9a74b58d", + Rssi3: -79, + Mac4: "e0286d8a9478", + Rssi4: -53, + Mac5: "f0b0140c96bb", + Rssi5: -46, + Mac6: "260122180d42", + Rssi6: -83, + }, + expectedStatus: &Status{ + DutyCycle: false, + ConfigChangeId: 0, + ConfigChangeSuccess: false, + Moving: false, + }, + port: 7, + }, + { + payload: "0c24651155ce55b8602232e20f52b0ac8ba91fedaaa5603197f93781a90aecdafa5fe8bc02ecdafa5fe8bd8c59c3c960f0a5", + expectedData: Port5Payload{ + Moving: false, + Mac1: "24651155ce55", + Rssi1: -72, + Mac2: "602232e20f52", + Rssi2: -80, + Mac3: "ac8ba91fedaa", + Rssi3: -91, + Mac4: "603197f93781", + Rssi4: -87, + Mac5: "0aecdafa5fe8", + Rssi5: -68, + Mac6: "02ecdafa5fe8", + Rssi6: -67, + Mac7: "8c59c3c960f0", + Rssi7: -91, + }, + expectedStatus: &Status{ + DutyCycle: false, + ConfigChangeId: 1, + ConfigChangeSuccess: true, + Moving: false, + }, + port: 5, + }, + } + + decoder := NewTagSLv1Decoder() + for _, test := range tests { + t.Run(fmt.Sprintf("TestFullDecodeWithPort%vAndPayload%v", test.port, test.payload), func(t *testing.T) { + data, status, err := decoder.Decode(test.payload, test.port, "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if data != test.expectedData { + t.Errorf("expected data: %v, got: %v", test.expectedData, data) + } + + if status != nil && test.expectedStatus == nil { + t.Errorf("expected status to be nil, got: %v", status) + } + if status == nil && test.expectedStatus != nil { + t.Errorf("expected status: %v, got: nil", test.expectedStatus) + } + }) + } +}