Another checkpoint moving to proto defined packets

This commit is contained in:
Daniel Pupius
2025-04-21 18:27:55 -07:00
parent 86380cccf9
commit f580249162
8 changed files with 280 additions and 281 deletions
+35 -25
View File
@@ -7,7 +7,7 @@ import (
"github.com/dpup/prefab/logging"
"meshstream/decoder"
meshtreampb "meshstream/generated/meshstream"
)
// TestBrokerSubscribeUnsubscribe tests the basic subscribe and unsubscribe functionality
@@ -38,8 +38,10 @@ func TestBrokerSubscribeUnsubscribe(t *testing.T) {
// First packet with ID 1
packet1 := &Packet{
DecodedPacket: &decoder.DecodedPacket{ID: 1},
TopicInfo: &decoder.TopicInfo{},
Packet: &meshtreampb.Packet{
Data: &meshtreampb.Data{Id: 1},
Info: &meshtreampb.TopicInfo{},
},
}
// Send the packet
@@ -48,8 +50,8 @@ func TestBrokerSubscribeUnsubscribe(t *testing.T) {
// Both subscribers should receive the packet
select {
case received := <-subscriber1:
if received.ID != 1 {
t.Errorf("Expected subscriber1 to receive packet with ID 1, got %d", received.ID)
if received.Data.Id != 1 {
t.Errorf("Expected subscriber1 to receive packet with ID 1, got %d", received.Data.Id)
}
case <-time.After(100 * time.Millisecond):
t.Error("subscriber1 didn't receive packet within timeout")
@@ -57,8 +59,8 @@ func TestBrokerSubscribeUnsubscribe(t *testing.T) {
select {
case received := <-subscriber2:
if received.ID != 1 {
t.Errorf("Expected subscriber2 to receive packet with ID 1, got %d", received.ID)
if received.Data.Id != 1 {
t.Errorf("Expected subscriber2 to receive packet with ID 1, got %d", received.Data.Id)
}
case <-time.After(100 * time.Millisecond):
t.Error("subscriber2 didn't receive packet within timeout")
@@ -78,8 +80,10 @@ func TestBrokerSubscribeUnsubscribe(t *testing.T) {
// Second packet with ID 2
packet2 := &Packet{
DecodedPacket: &decoder.DecodedPacket{ID: 2},
TopicInfo: &decoder.TopicInfo{},
Packet: &meshtreampb.Packet{
Data: &meshtreampb.Data{Id: 2},
Info: &meshtreampb.TopicInfo{},
},
}
// Send the second packet
@@ -88,8 +92,8 @@ func TestBrokerSubscribeUnsubscribe(t *testing.T) {
// The second subscriber should receive the packet
select {
case received := <-subscriber2:
if received.ID != 2 {
t.Errorf("Expected subscriber2 to receive packet with ID 2, got %d", received.ID)
if received.Data.Id != 2 {
t.Errorf("Expected subscriber2 to receive packet with ID 2, got %d", received.Data.Id)
}
case <-time.After(100 * time.Millisecond):
t.Error("subscriber2 didn't receive second packet within timeout")
@@ -115,8 +119,10 @@ func TestBrokerMultipleSubscribers(t *testing.T) {
// Send a test packet with ID 42
testPacket := &Packet{
DecodedPacket: &decoder.DecodedPacket{ID: 42},
TopicInfo: &decoder.TopicInfo{},
Packet: &meshtreampb.Packet{
Data: &meshtreampb.Data{Id: 42},
Info: &meshtreampb.TopicInfo{},
},
}
sourceChan <- testPacket
@@ -129,8 +135,8 @@ func TestBrokerMultipleSubscribers(t *testing.T) {
defer wg.Done()
select {
case received := <-ch:
if received.ID != 42 {
t.Errorf("subscriber %d expected packet ID 42, got %d", idx, received.ID)
if received.Data.Id != 42 {
t.Errorf("subscriber %d expected packet ID 42, got %d", idx, received.Data.Id)
}
case <-time.After(100 * time.Millisecond):
t.Errorf("subscriber %d didn't receive packet within timeout", idx)
@@ -169,12 +175,16 @@ func TestBrokerSlowSubscriber(t *testing.T) {
// Send two packets quickly to fill the slow subscriber's buffer
testPacket1 := &Packet{
DecodedPacket: &decoder.DecodedPacket{ID: 101},
TopicInfo: &decoder.TopicInfo{},
Packet: &meshtreampb.Packet{
Data: &meshtreampb.Data{Id: 101},
Info: &meshtreampb.TopicInfo{},
},
}
testPacket2 := &Packet{
DecodedPacket: &decoder.DecodedPacket{ID: 102},
TopicInfo: &decoder.TopicInfo{},
Packet: &meshtreampb.Packet{
Data: &meshtreampb.Data{Id: 102},
Info: &meshtreampb.TopicInfo{},
},
}
sourceChan <- testPacket1
@@ -187,8 +197,8 @@ func TestBrokerSlowSubscriber(t *testing.T) {
// The normal subscriber should receive both packets
select {
case received := <-normalSubscriber:
if received.ID != 101 {
t.Errorf("normalSubscriber expected packet ID 101, got %d", received.ID)
if received.Data.Id != 101 {
t.Errorf("normalSubscriber expected packet ID 101, got %d", received.Data.Id)
}
case <-time.After(100 * time.Millisecond):
t.Error("normalSubscriber didn't receive first packet within timeout")
@@ -196,8 +206,8 @@ func TestBrokerSlowSubscriber(t *testing.T) {
select {
case received := <-normalSubscriber:
if received.ID != 102 {
t.Errorf("normalSubscriber expected packet ID 102, got %d", received.ID)
if received.Data.Id != 102 {
t.Errorf("normalSubscriber expected packet ID 102, got %d", received.Data.Id)
}
case <-time.After(100 * time.Millisecond):
t.Error("normalSubscriber didn't receive second packet within timeout")
@@ -206,8 +216,8 @@ func TestBrokerSlowSubscriber(t *testing.T) {
// The slow subscriber should receive at least the first packet
select {
case received := <-slowSubscriber:
if received.ID != 101 {
t.Errorf("slowSubscriber expected packet ID 101, got %d", received.ID)
if received.Data.Id != 101 {
t.Errorf("slowSubscriber expected packet ID 101, got %d", received.Data.Id)
}
case <-time.After(100 * time.Millisecond):
t.Error("slowSubscriber didn't receive first packet within timeout")
+3 -4
View File
@@ -8,7 +8,6 @@ import (
mqtt "github.com/eclipse/paho.mqtt.golang"
"meshstream/decoder"
mesh "meshstream/proto/generated"
)
// Config holds configuration for the MQTT client
@@ -99,10 +98,10 @@ func (c *Client) messageHandler(client mqtt.Client, msg mqtt.Message) {
switch topicInfo.Format {
case "e", "c", "map":
// Binary encoded protobuf message
decodedPacket := decoder.DecodeMessage(msg.Payload(), topicInfo)
data := decoder.DecodeMessage(msg.Payload(), topicInfo)
// Create packet with both the decoded packet and topic info
packet := NewPacket(decodedPacket, topicInfo)
// Create packet with both the data and topic info
packet := NewPacket(data, topicInfo)
// Send the decoded message to the channel, but don't block if buffer is full
select {
+23 -23
View File
@@ -6,7 +6,7 @@ import (
"github.com/dpup/prefab/logging"
pb "meshstream/proto/generated/meshtastic"
pb "meshstream/generated/meshtastic"
)
// MessageLogger logs messages using the provided logger
@@ -44,23 +44,23 @@ func NewMessageLogger(broker *Broker, briefMode bool, logger logging.Logger) (*M
func (ml *MessageLogger) getBriefSummary(packet *Packet) string {
var summary string
if packet.DecodedPacket.DecodeError != nil {
return fmt.Sprintf("Error decoding packet: %v", packet.DecodedPacket.DecodeError)
if packet.Data.DecodeError != "" {
return fmt.Sprintf("Error decoding packet: %v", packet.Data.DecodeError)
}
// Create a basic summary based on the port type
switch packet.PortNum {
switch packet.Data.PortNum {
case pb.PortNum_TEXT_MESSAGE_APP:
// For text messages, include the text content
if text, ok := packet.Payload.(string); ok {
summary = fmt.Sprintf("Text message: %s", text)
if packet.Data.GetTextMessage() != "" {
summary = fmt.Sprintf("Text message: %s", packet.Data.GetTextMessage())
} else {
summary = "Text message (invalid format)"
}
case pb.PortNum_POSITION_APP:
// For position messages, include a compact location summary
if pos, ok := packet.Payload.(*pb.Position); ok {
if pos := packet.Data.GetPosition(); pos != nil {
lat := float64(pos.GetLatitudeI()) / 10000000.0
lon := float64(pos.GetLongitudeI()) / 10000000.0
summary = fmt.Sprintf("Position: %.5f, %.5f", lat, lon)
@@ -70,7 +70,7 @@ func (ml *MessageLogger) getBriefSummary(packet *Packet) string {
case pb.PortNum_TELEMETRY_APP:
// For telemetry, give a short summary of what's included
if telemetry, ok := packet.Payload.(*pb.Telemetry); ok {
if telemetry := packet.Data.GetTelemetry(); telemetry != nil {
parts := []string{}
if telemetry.GetEnvironmentMetrics() != nil {
parts = append(parts, "environment")
@@ -88,7 +88,7 @@ func (ml *MessageLogger) getBriefSummary(packet *Packet) string {
default:
// For other types, just mention the port type
summary = fmt.Sprintf("Message type: %s", packet.PortNum.String())
summary = fmt.Sprintf("Message type: %s", packet.Data.PortNum.String())
}
return summary
@@ -100,30 +100,30 @@ func (ml *MessageLogger) logMessage(packet *Packet) {
briefSummary := ml.getBriefSummary(packet)
// Build the message prefix with type and GatewayID info for brief mode
typePrefix := fmt.Sprintf("[%s]", packet.PortNum.String())
if packet.GatewayID != "" {
briefSummary = fmt.Sprintf("%s Gateway:%s %s", typePrefix, packet.GatewayID, briefSummary)
typePrefix := fmt.Sprintf("[%s]", packet.Data.PortNum.String())
if packet.Data.GatewayId != "" {
briefSummary = fmt.Sprintf("%s Gateway:%s %s", typePrefix, packet.Data.GatewayId, briefSummary)
} else {
briefSummary = fmt.Sprintf("%s %s", typePrefix, briefSummary)
}
if ml.briefMode {
fields := []interface{}{
"portNum", packet.PortNum.String(),
"from", packet.From,
"to", packet.To,
"gateway", packet.GatewayID,
"channel", packet.TopicInfo.Channel,
"region", packet.TopicInfo.RegionPath,
"hopLimit", packet.HopLimit,
"id", packet.ID,
"portNum", packet.Data.PortNum.String(),
"from", packet.Data.From,
"to", packet.Data.To,
"gateway", packet.Data.GatewayId,
"channel", packet.Info.Channel,
"region", packet.Info.RegionPath,
"hopLimit", packet.Data.HopLimit,
"id", packet.Data.Id,
}
if packet.DecodedPacket.DecodeError != nil {
fields = append(fields, "error", packet.DecodedPacket.DecodeError.Error())
if packet.Data.DecodeError != "" {
fields = append(fields, "error", packet.Data.DecodeError)
}
ml.logger.Infow(briefSummary, fields...)
} else {
ml.logger.Infow(briefSummary, "packet", packet)
}
}
}
+11 -145
View File
@@ -1,151 +1,17 @@
package mqtt
import (
mesh "meshstream/proto/generated"
meshtreampb "meshstream/generated/meshstream"
)
// Packet extends the DecodedPacket with MQTT topic information
type Packet struct {
*mesh.Packet
// Type alias for proto packet.
type Packet meshtreampb.Packet
// NewPacket creates a Packet from a data packet and topic info
func NewPacket(data *meshtreampb.Data, topicInfo *meshtreampb.TopicInfo) *Packet {
p := Packet(meshtreampb.Packet{
Data: data,
Info: topicInfo,
})
return &p
}
// NewPacket creates a Packet from a decoded packet and topic info
func NewPacket(decoded *mesh.DecodedPacket, topicInfo *mesh.TopicInfo) *Packet {
return &Packet{
Packet: &mesh.Packet{
DecodedPacket: decoded,
TopicInfo: topicInfo,
},
}
}
// Helper accessors to maintain backward compatibility with existing code
func (p *Packet) GetChannelID() string {
if p.DecodedPacket != nil {
return p.DecodedPacket.ChannelId
}
return ""
}
func (p *Packet) GetGatewayID() string {
if p.DecodedPacket != nil {
return p.DecodedPacket.GatewayId
}
return ""
}
func (p *Packet) GetID() uint32 {
if p.DecodedPacket != nil {
return p.DecodedPacket.Id
}
return 0
}
func (p *Packet) GetFrom() uint32 {
if p.DecodedPacket != nil {
return p.DecodedPacket.From
}
return 0
}
func (p *Packet) GetTo() uint32 {
if p.DecodedPacket != nil {
return p.DecodedPacket.To
}
return 0
}
func (p *Packet) GetPortNum() int32 {
if p.DecodedPacket != nil {
return int32(p.DecodedPacket.PortNum)
}
return 0
}
func (p *Packet) GetPortNumString() string {
if p.DecodedPacket != nil {
return p.DecodedPacket.PortNum.String()
}
return "UNKNOWN"
}
func (p *Packet) GetPayload() interface{} {
if p.DecodedPacket == nil {
return nil
}
// Depending on the payload type, return the appropriate value
switch x := p.DecodedPacket.Payload.(type) {
case *mesh.DecodedPacket_TextMessage:
return x.TextMessage
case *mesh.DecodedPacket_BinaryData:
return x.BinaryData
case *mesh.DecodedPacket_Position:
return x.Position
case *mesh.DecodedPacket_NodeInfo:
return x.NodeInfo
case *mesh.DecodedPacket_Telemetry:
return x.Telemetry
case *mesh.DecodedPacket_Waypoint:
return x.Waypoint
case *mesh.DecodedPacket_RouteDiscovery:
return x.RouteDiscovery
case *mesh.DecodedPacket_NeighborInfo:
return x.NeighborInfo
case *mesh.DecodedPacket_CompressedText:
return x.CompressedText
case *mesh.DecodedPacket_MapReport:
return x.MapReport
default:
return nil
}
}
func (p *Packet) GetHopLimit() uint32 {
if p.DecodedPacket != nil {
return p.DecodedPacket.HopLimit
}
return 0
}
func (p *Packet) GetHopStart() uint32 {
if p.DecodedPacket != nil {
return p.DecodedPacket.HopStart
}
return 0
}
func (p *Packet) HasDecodeError() bool {
return p.DecodedPacket != nil && p.DecodedPacket.DecodeError != ""
}
func (p *Packet) GetDecodeError() string {
if p.DecodedPacket != nil {
return p.DecodedPacket.DecodeError
}
return ""
}
// GetFullTopic returns the MQTT topic this packet was received on
func (p *Packet) GetFullTopic() string {
if p.TopicInfo != nil {
return p.TopicInfo.FullTopic
}
return ""
}
// GetRegionPath returns the region path from the topic
func (p *Packet) GetRegionPath() string {
if p.TopicInfo != nil {
return p.TopicInfo.RegionPath
}
return ""
}
// GetChannel returns the channel name from the topic
func (p *Packet) GetChannel() string {
if p.TopicInfo != nil {
return p.TopicInfo.Channel
}
return ""
}
+4 -4
View File
@@ -7,7 +7,7 @@ import (
"github.com/dpup/prefab/logging"
pb "meshstream/proto/generated/meshtastic"
pb "meshstream/generated/meshtastic"
)
// MessageStats tracks statistics about received messages
@@ -71,10 +71,10 @@ func (s *MessageStats) recordMessage(packet *Packet) {
s.TotalMessages++
// Count by source node
s.ByNode[packet.From]++
s.ByNode[packet.Data.From]++
// Count by port type
s.ByPortType[packet.PortNum]++
s.ByPortType[packet.Data.PortNum]++
}
// PrintStats logs current statistics using the structured logger
@@ -122,4 +122,4 @@ func (s *MessageStats) PrintStats() {
s.ByNode = make(map[uint32]int)
s.ByPortType = make(map[pb.PortNum]int)
s.LastStatsPrinted = now
}
}