diff --git a/Makefile b/Makefile index e9ed747..266270c 100644 --- a/Makefile +++ b/Makefile @@ -68,4 +68,7 @@ check-metrics: echo "✅ All Prometheus metrics are correctly prefixed."; \ fi -.PHONY: check-coverage check-json-tags check-metrics +test: + go test ./... + +.PHONY: check-coverage check-json-tags check-metrics test diff --git a/pkg/decoder/decoder.go b/pkg/decoder/decoder.go index 2152a15..46e6b09 100644 --- a/pkg/decoder/decoder.go +++ b/pkg/decoder/decoder.go @@ -190,5 +190,5 @@ type UplinkFeatureRotationState interface { } type UplinkFeatureSequenceNumber interface { - GetSequenceNumber() uint + GetSequenceNumber() *uint16 } diff --git a/pkg/decoder/decoder_test.go b/pkg/decoder/decoder_test.go index 7ce72dc..4003be7 100644 --- a/pkg/decoder/decoder_test.go +++ b/pkg/decoder/decoder_test.go @@ -175,4 +175,4 @@ func (*dummyRotationState) GetDuration() time.Duration { return 0 } type dummySequenceNumber struct{} -func (*dummySequenceNumber) GetSequenceNumber() uint { return 0 } +func (*dummySequenceNumber) GetSequenceNumber() *uint16 { return nil } diff --git a/pkg/decoder/smartlabel/v2/port180.go b/pkg/decoder/smartlabel/v2/port180.go new file mode 100644 index 0000000..fa5f1c8 --- /dev/null +++ b/pkg/decoder/smartlabel/v2/port180.go @@ -0,0 +1,55 @@ +package smartlabel + +import ( + "time" + + "github.com/truvami/decoder/pkg/decoder" +) + +// +------+------+-------------------------------------------+------------------------+ +// | Byte | Size | Description | Format | +// +------+------+-------------------------------------------+------------------------+ +// | 0 | 1 | EOG (End of group) | uint1 | +// | | | RFU (Reserved for future use) | uint2 | +// | | | GRP_TOKEN (Group token) | uint5 | +// | 1 | n | U-GNSSLOC-NAV message(s) | | +// +------+------+-------------------------------------------+------------------------+ + +type Port180Payload struct { + EndOfGroup bool `json:"endOfGroup"` + GroupToken uint8 `json:"groupToken" validate:"gte=2, lte=31"` + NavMessage []byte `json:"navMessage"` + Latitude float64 `json:"latitude" validate:"gte=-90,lte=90"` + Longitude float64 `json:"longitude" validate:"gte=-180,lte=180"` + Altitude float64 `json:"altitude"` +} + +var _ decoder.UplinkFeatureGNSS = &Port180Payload{} + +func (p Port180Payload) GetLatitude() float64 { + return p.Latitude +} + +func (p Port180Payload) GetLongitude() float64 { + return p.Longitude +} + +func (p Port180Payload) GetAltitude() float64 { + return p.Altitude +} + +func (p Port180Payload) GetAccuracy() *float64 { + return nil +} + +func (p Port180Payload) GetTTF() *time.Duration { + return nil +} + +func (p Port180Payload) GetPDOP() *float64 { + return nil +} + +func (p Port180Payload) GetSatellites() *uint8 { + return nil +} diff --git a/pkg/decoder/smartlabel/v2/port180_test.go b/pkg/decoder/smartlabel/v2/port180_test.go new file mode 100644 index 0000000..f11693f --- /dev/null +++ b/pkg/decoder/smartlabel/v2/port180_test.go @@ -0,0 +1,59 @@ +package smartlabel + +import ( + "testing" + "time" +) + +func TestPort180Payload_GNSSMethods(t *testing.T) { + t.Parallel() + + p := Port180Payload{ + EndOfGroup: true, + GroupToken: 10, + NavMessage: []byte{0x01, 0x02, 0x03}, + Latitude: 47.3769, + Longitude: 8.5417, + Altitude: 500.0, + } + + if got := p.GetLatitude(); got != p.Latitude { + t.Fatalf("GetLatitude() = %v, want %v", got, p.Latitude) + } + if got := p.GetLongitude(); got != p.Longitude { + t.Fatalf("GetLongitude() = %v, want %v", got, p.Longitude) + } + if got := p.GetAltitude(); got != p.Altitude { + t.Fatalf("GetAltitude() = %v, want %v", got, p.Altitude) + } + if got := p.GetAccuracy(); got != nil { + t.Fatalf("GetAccuracy() = %v, want nil", got) + } + if got := p.GetTTF(); got != nil { + t.Fatalf("GetTTF() = %v, want nil", got) + } + if got := p.GetPDOP(); got != nil { + t.Fatalf("GetPDOP() = %v, want nil", got) + } + if got := p.GetSatellites(); got != nil { + t.Fatalf("GetSatellites() = %v, want nil", got) + } +} + +// Compile-time like assertion helpers (executed to ensure method signatures remain compatible). +func TestPort180Payload_InterfaceSatisfaction(t *testing.T) { + t.Parallel() + + var _ = func() any { + var _ interface { + GetLatitude() float64 + GetLongitude() float64 + GetAltitude() float64 + GetAccuracy() *float64 + GetTTF() *time.Duration + GetPDOP() *float64 + GetSatellites() *uint8 + } = Port180Payload{} + return nil + }() +} diff --git a/pkg/decoder/smartlabel/v2/port190.go b/pkg/decoder/smartlabel/v2/port190.go new file mode 100644 index 0000000..ad20479 --- /dev/null +++ b/pkg/decoder/smartlabel/v2/port190.go @@ -0,0 +1,175 @@ +package smartlabel + +import ( + "time" + + "github.com/truvami/decoder/pkg/decoder" +) + +// Moving / Steady +// +------+------+-----------------------------------------------+------------+ +// | Byte | Size | Description | Format | +// +------+------+-----------------------------------------------+------------+ +// | 0 | 1 | tag (0x10 = moving, 0x18 = steady) | byte | +// | 1 | 1 | rssi signal 1 | int8 | +// | 2 | 6 | mac address signal 1 | byte[6] | +// | 8 | 1 | rssi signal 2 | int8 | +// | 9 | 6 | mac address signal 2 | byte[6] | +// | 15 | 1 | rssi signal 3 | int8 | +// | 16 | 6 | mac address signal 3 | byte[6] | +// | 22 | 1 | rssi signal 4 | int8 | +// | 23 | 6 | mac address signal 4 | byte[6] | +// | 29 | 1 | rssi signal 5 | int8 | +// | 30 | 6 | mac address signal 5 | byte[6] | +// +------+------+-----------------------------------------------+------------+ +// +// Moving / Steady with timestamp +// +------+------+-----------------------------------------------+------------+ +// | Byte | Size | Description | Format | +// +------+------+-----------------------------------------------+------------+ +// | 0 | 1 | tag (0x14 = moving, 0x1C = steady) | byte | +// | 1 | 4 | timestamp | uint32 | +// | 5 | 1 | rssi signal 1 | int8 | +// | 6 | 6 | mac address signal 1 | byte[6] | +// | 12 | 1 | rssi signal 2 | int8 | +// | 13 | 6 | mac address signal 2 | byte[6] | +// | 19 | 1 | rssi signal 3 | int8 | +// | 20 | 6 | mac address signal 3 | byte[6] | +// | 26 | 1 | rssi signal 4 | int8 | +// | 27 | 6 | mac address signal 4 | byte[6] | +// | 33 | 1 | rssi signal 5 | int8 | +// | 34 | 6 | mac address signal 5 | byte[6] | +// +------+------+-----------------------------------------------+------------+ +// +// Moving / Steady with sequence number +// +------+------+-----------------------------------------------+------------+ +// | Byte | Size | Description | Format | +// +------+------+-----------------------------------------------+------------+ +// | 0 | 1 | tag (0x80 = moving, 0x88 = steady) | byte | +// | 1 | 2 | sequence number | uint16 | +// | 3 | 1 | rssi signal 1 | int8 | +// | 4 | 6 | mac address signal 1 | byte[6] | +// | 10 | 1 | rssi signal 2 | int8 | +// | 11 | 6 | mac address signal 2 | byte[6] | +// | 17 | 1 | rssi signal 3 | int8 | +// | 18 | 6 | mac address signal 3 | byte[6] | +// | 24 | 1 | rssi signal 4 | int8 | +// | 25 | 6 | mac address signal 4 | byte[6] | +// | 31 | 1 | rssi signal 5 | int8 | +// | 32 | 6 | mac address signal 5 | byte[6] | +// +------+------+-----------------------------------------------+------------+ +// +// Moving / Steady with sequence number and timestamp +// +------+------+-----------------------------------------------+------------+ +// | Byte | Size | Description | Format | +// +------+------+-----------------------------------------------+------------+ +// | 0 | 1 | tag (0x84 = moving, 0x8C = steady) | byte | +// | 1 | 2 | sequence number | uint16 | +// | 3 | 4 | timestamp | uint32 | +// | 7 | 1 | rssi signal 1 | int8 | +// | 8 | 6 | mac address signal 1 | byte[6] | +// | 14 | 1 | rssi signal 2 | int8 | +// | 15 | 6 | mac address signal 2 | byte[6] | +// | 21 | 1 | rssi signal 3 | int8 | +// | 22 | 6 | mac address signal 3 | byte[6] | +// | 28 | 1 | rssi signal 4 | int8 | +// | 29 | 6 | mac address signal 4 | byte[6] | +// | 35 | 1 | rssi signal 5 | int8 | +// | 36 | 6 | mac address signal 5 | byte[6] | +// +------+------+-----------------------------------------------+------------+ + +const ( + Port190Moving = 0x10 + Port190MovingTimestamp = 0x14 + Port190Steady = 0x18 + Port190SteadyTimestamp = 0x1c + Port190SeqMoving = 0x80 + Port190SeqMovingTimestamp = 0x84 + Port190SeqSteady = 0x88 + Port190SeqSteadyTimestamp = 0x8c +) + +type Port190Payload struct { + Tag byte `json:"tag" validate:"gte=0,lte=1"` + SequenceNumber *uint16 `json:"sequenceNumber"` + Timestamp *time.Time `json:"timestamp"` + Rssi1 *int8 `json:"rssi1" validate:"gte=-120,lte=-20"` + Mac1 string `json:"mac1"` + Rssi2 *int8 `json:"rssi2" validate:"gte=-120,lte=-20"` + Mac2 *string `json:"mac2"` + Rssi3 *int8 `json:"rssi3" validate:"gte=-120,lte=-20"` + Mac3 *string `json:"mac3"` + Rssi4 *int8 `json:"rssi4" validate:"gte=-120,lte=-20"` + Mac4 *string `json:"mac4"` + Rssi5 *int8 `json:"rssi5" validate:"gte=-120,lte=-20"` + Mac5 *string `json:"mac5"` +} + +var _ decoder.UplinkFeatureWiFi = &Port190Payload{} +var _ decoder.UplinkFeatureMoving = &Port190Payload{} +var _ decoder.UplinkFeatureSequenceNumber = &Port190Payload{} +var _ decoder.UplinkFeatureTimestamp = &Port190Payload{} + +func (p Port190Payload) GetAccessPoints() []decoder.AccessPoint { + accessPoints := []decoder.AccessPoint{} + + if p.Mac1 != "" { + accessPoints = append(accessPoints, decoder.AccessPoint{ + MAC: p.Mac1, + RSSI: p.Rssi1, + }) + } + + if p.Mac2 != nil { + accessPoints = append(accessPoints, decoder.AccessPoint{ + MAC: *p.Mac2, + RSSI: p.Rssi2, + }) + } + + if p.Mac3 != nil { + accessPoints = append(accessPoints, decoder.AccessPoint{ + MAC: *p.Mac3, + RSSI: p.Rssi3, + }) + } + + if p.Mac4 != nil { + accessPoints = append(accessPoints, decoder.AccessPoint{ + MAC: *p.Mac4, + RSSI: p.Rssi4, + }) + } + + if p.Mac5 != nil { + accessPoints = append(accessPoints, decoder.AccessPoint{ + MAC: *p.Mac5, + RSSI: p.Rssi5, + }) + } + + return accessPoints +} + +func (p Port190Payload) IsMoving() bool { + switch p.Tag { + case Port190Moving: + return true + case Port190MovingTimestamp: + return true + case Port190SeqMoving: + return true + case Port190SeqMovingTimestamp: + return true + } + + return false +} + +func (p Port190Payload) GetTimestamp() *time.Time { + return p.Timestamp +} + +func (p Port190Payload) GetSequenceNumber() *uint16 { + return p.SequenceNumber +} diff --git a/pkg/decoder/tagxl/v1/port152.go b/pkg/decoder/tagxl/v1/port152.go index b259205..bf8a990 100644 --- a/pkg/decoder/tagxl/v1/port152.go +++ b/pkg/decoder/tagxl/v1/port152.go @@ -90,8 +90,9 @@ func (p Port152Payload) GetDuration() time.Duration { return time.Duration(int64(p.ElapsedSeconds)) * time.Second } -func (p Port152Payload) GetSequenceNumber() uint { - return uint(p.SequenceNumber) +func (p Port152Payload) GetSequenceNumber() *uint16 { + v := uint16(p.SequenceNumber) + return &v } func byteToRotationState(b uint8) decoder.RotationState {