package decoder import ( "fmt" "strings" meshtreampb "meshstream/generated/meshstream" ) // FormatTopicAndPacket creates a human-readable representation of a topic and packet // for debugging purposes. func FormatTopicAndPacket(topic *meshtreampb.TopicInfo, data *meshtreampb.Data) string { var sb strings.Builder // Topic information sb.WriteString("===== Topic Info =====\n") sb.WriteString(fmt.Sprintf("Full Topic: %s\n", topic.FullTopic)) sb.WriteString(fmt.Sprintf("Region Path: %s\n", topic.RegionPath)) sb.WriteString(fmt.Sprintf("Version: %s\n", topic.Version)) sb.WriteString(fmt.Sprintf("Format: %s\n", topic.Format)) sb.WriteString(fmt.Sprintf("Channel: %s\n", topic.Channel)) sb.WriteString(fmt.Sprintf("User ID: %s\n", topic.UserId)) // Packet information sb.WriteString("\n===== Packet Info =====\n") // Check if we have a decode error if data.DecodeError != "" { sb.WriteString(fmt.Sprintf("ERROR: %s\n", data.DecodeError)) return sb.String() } // Basic packet information sb.WriteString(fmt.Sprintf("ID: %d\n", data.Id)) sb.WriteString(fmt.Sprintf("From: %d\n", data.From)) sb.WriteString(fmt.Sprintf("To: %d\n", data.To)) sb.WriteString(fmt.Sprintf("Channel ID: %s\n", data.ChannelId)) sb.WriteString(fmt.Sprintf("Gateway ID: %s\n", data.GatewayId)) sb.WriteString(fmt.Sprintf("Port: %s\n", data.PortNum.String())) sb.WriteString(fmt.Sprintf("Hop Limit: %d\n", data.HopLimit)) sb.WriteString(fmt.Sprintf("Request ID: %d\n", data.RequestId)) // Payload type-specific information sb.WriteString("\n===== Payload Info =====\n") switch data.Payload.(type) { case *meshtreampb.Data_TextMessage: sb.WriteString(fmt.Sprintf("Type: Text Message\nContent: %s\n", data.GetTextMessage())) case *meshtreampb.Data_Position: pos := data.GetPosition() lat := float64(pos.GetLatitudeI()) / 10000000.0 lon := float64(pos.GetLongitudeI()) / 10000000.0 sb.WriteString(fmt.Sprintf("Type: Position\nLatitude: %.6f\nLongitude: %.6f\nAltitude: %d\n", lat, lon, pos.GetAltitude())) case *meshtreampb.Data_Telemetry: telemetry := data.GetTelemetry() sb.WriteString("Type: Telemetry\n") if telemetry.GetEnvironmentMetrics() != nil { env := telemetry.GetEnvironmentMetrics() sb.WriteString(fmt.Sprintf("Environment: Temp %.1f°C, Rel Humidity %.1f%%\n", env.GetTemperature(), env.GetRelativeHumidity())) } if telemetry.GetDeviceMetrics() != nil { dev := telemetry.GetDeviceMetrics() sb.WriteString(fmt.Sprintf("Device: Battery %d%%, Voltage %.1fV\n", dev.GetBatteryLevel(), dev.GetVoltage())) } case *meshtreampb.Data_NodeInfo: user := data.GetNodeInfo() sb.WriteString(fmt.Sprintf("Type: User Info\nID: %s\nLongName: %s\nShortName: %s\n", user.GetId(), user.GetLongName(), user.GetShortName())) case *meshtreampb.Data_MapReport: sb.WriteString(fmt.Sprintf("Type: Map Report\n")) case *meshtreampb.Data_BinaryData: sb.WriteString(fmt.Sprintf("Type: Binary Data\nLength: %d bytes\n", len(data.GetBinaryData()))) default: sb.WriteString("Type: Unknown\n") } return sb.String() }