Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions pkg/decoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions pkg/decoder/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,7 @@ func UintToBinaryArray(value uint64, length int) []byte {
}
return binaryArray
}

func ToIntPointer(value int) *int {
return &value
}
55 changes: 29 additions & 26 deletions pkg/decoder/tagsl/v1/decoder.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package tagsl

import (
"encoding/hex"
"fmt"
"reflect"

Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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{
Expand All @@ -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{
Expand All @@ -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{
Expand All @@ -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{
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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{
Expand All @@ -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{
Expand All @@ -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{
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}
Expand All @@ -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)
Expand Down
131 changes: 113 additions & 18 deletions pkg/decoder/tagsl/v1/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -542,7 +542,7 @@ func TestParseStatusByte(t *testing.T) {
err: nil,
},
{
hexInput: "FF",
input: 0xFF,
expected: Status{
DutyCycle: true,
ConfigChangeId: 15,
Expand All @@ -552,7 +552,7 @@ func TestParseStatusByte(t *testing.T) {
err: nil,
},
{
hexInput: "00",
input: 0x00,
expected: Status{
DutyCycle: false,
ConfigChangeId: 0,
Expand All @@ -562,7 +562,7 @@ func TestParseStatusByte(t *testing.T) {
err: nil,
},
{
hexInput: "4A",
input: 0x4A,
expected: Status{
DutyCycle: false,
ConfigChangeId: 9,
Expand All @@ -572,7 +572,7 @@ func TestParseStatusByte(t *testing.T) {
err: nil,
},
{
hexInput: "8D",
input: 0x8D,
expected: Status{
DutyCycle: true,
ConfigChangeId: 1,
Expand All @@ -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)
}
Expand All @@ -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)
}
})
}
}