Track rx time

This commit is contained in:
Daniel Pupius
2025-04-23 13:59:54 -07:00
parent 6c891198b5
commit 489f995433
7 changed files with 79 additions and 11 deletions

View File

@@ -3,6 +3,7 @@ package decoder
import (
"fmt"
"strings"
"time"
"google.golang.org/protobuf/proto"
@@ -77,7 +78,10 @@ func DecodeEncodedMessage(payload []byte) (*pb.ServiceEnvelope, error) {
// DecodeMessage creates a Data object from a binary encoded message
func DecodeMessage(payload []byte, topicInfo *meshtreampb.TopicInfo) *meshtreampb.Data {
data := &meshtreampb.Data{}
data := &meshtreampb.Data{
// Add reception timestamp (Unix timestamp in seconds)
RxTime: uint64(time.Now().Unix()),
}
// First decode the envelope
envelope, err := DecodeEncodedMessage(payload)

View File

@@ -220,7 +220,9 @@ type Data struct {
Source uint32 `protobuf:"varint,54,opt,name=source,proto3" json:"source,omitempty"`
WantResponse bool `protobuf:"varint,55,opt,name=want_response,json=wantResponse,proto3" json:"want_response,omitempty"`
// Error tracking
DecodeError string `protobuf:"bytes,60,opt,name=decode_error,json=decodeError,proto3" json:"decode_error,omitempty"`
DecodeError string `protobuf:"bytes,60,opt,name=decode_error,json=decodeError,proto3" json:"decode_error,omitempty"`
// Reception timestamp (added by decoder)
RxTime uint64 `protobuf:"varint,61,opt,name=rx_time,json=rxTime,proto3" json:"rx_time,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -654,6 +656,13 @@ func (x *Data) GetDecodeError() string {
return ""
}
func (x *Data) GetRxTime() uint64 {
if x != nil {
return x.RxTime
}
return 0
}
type isData_Payload interface {
isData_Payload()
}
@@ -843,7 +852,7 @@ const file_meshstream_meshstream_proto_rawDesc = "" +
"\aversion\x18\x03 \x01(\tR\aversion\x12\x16\n" +
"\x06format\x18\x04 \x01(\tR\x06format\x12\x18\n" +
"\achannel\x18\x05 \x01(\tR\achannel\x12\x17\n" +
"\auser_id\x18\x06 \x01(\tR\x06userId\"\xfb\r\n" +
"\auser_id\x18\x06 \x01(\tR\x06userId\"\x94\x0e\n" +
"\x04Data\x12\x1d\n" +
"\n" +
"channel_id\x18\x01 \x01(\tR\tchannelId\x12\x1d\n" +
@@ -906,7 +915,8 @@ const file_meshstream_meshstream_proto_rawDesc = "" +
"\x04dest\x185 \x01(\rR\x04dest\x12\x16\n" +
"\x06source\x186 \x01(\rR\x06source\x12#\n" +
"\rwant_response\x187 \x01(\bR\fwantResponse\x12!\n" +
"\fdecode_error\x18< \x01(\tR\vdecodeErrorB\t\n" +
"\fdecode_error\x18< \x01(\tR\vdecodeError\x12\x17\n" +
"\arx_time\x18= \x01(\x04R\x06rxTimeB\t\n" +
"\apayloadB(Z&proto/generated/meshstream;meshtreampbb\x06proto3"
var (

View File

@@ -91,4 +91,7 @@ message Data {
// Error tracking
string decode_error = 60;
// Reception timestamp (added by decoder)
uint64 rx_time = 61;
}

View File

@@ -43,6 +43,36 @@ export const PacketList: React.FC = () => {
},
[hashString]
);
// Get the earliest reception time from the packets
const getEarliestTime = useCallback((): string => {
if (packets.length === 0) return "";
// Find the packet with the earliest rxTime or time
let earliestTime: number | undefined;
packets.forEach(packet => {
// Check for rxTime first, then fall back to other timestamp fields
const packetTime = packet.data.rxTime ||
(packet.data.telemetry?.time) ||
undefined;
if (packetTime && (!earliestTime || packetTime < earliestTime)) {
earliestTime = packetTime;
}
});
if (!earliestTime) {
return "unknown time";
}
// Format the time in a nice way
const date = new Date(earliestTime * 1000);
return date.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
});
}, [packets]);
// We don't need to track packet keys in state anymore since we use data.id
// and it's deterministic - removing this effect to prevent the infinite loop issue
@@ -102,7 +132,12 @@ export const PacketList: React.FC = () => {
<div>
<div className="flex justify-between items-center mb-2">
<div className="text-sm text-neutral-400 px-2">
{packets.length} packets received, since 6:00am
{packets.length} packets received
{packets.length > 0 && (
<>
, since {getEarliestTime()}
</>
)}
</div>
<div className="flex items-center space-x-3">
{/* Show buffered count when paused */}

View File

@@ -53,9 +53,19 @@ export const PacketCard: React.FC<PacketCardProps> = ({
)}
</div>
{/* Right side: ID and Type */}
{/* Right side: ID, Time, and Type */}
<div className="flex items-center gap-3 text-xs">
<span className="text-neutral-400">{data.id || "None"}</span>
<div className="flex items-center">
<span className="text-neutral-400">{data.id || "None"}</span>
{data.rxTime && (
<span className="text-neutral-500 ml-2">
{new Date(data.rxTime * 1000).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
})}
</span>
)}
</div>
<span className="px-2 py-0.5 bg-neutral-700/50 text-neutral-300 rounded-full text-xs">
{label}
</span>

View File

@@ -26,13 +26,16 @@ export const TelemetryPacket: React.FC<TelemetryPacketProps> = ({ packet }) => {
key => telemetry.environmentMetrics![key as keyof typeof telemetry.environmentMetrics] !== undefined
);
// Use the reception timestamp if available, otherwise fall back to the telemetry time
const timestamp = data.rxTime || telemetry.time;
// Return the appropriate component based on telemetry type
if (hasDeviceMetrics && telemetry.deviceMetrics) {
return (
<DeviceMetricsPacket
packet={packet}
metrics={telemetry.deviceMetrics}
timestamp={telemetry.time}
timestamp={timestamp}
/>
);
}
@@ -42,7 +45,7 @@ export const TelemetryPacket: React.FC<TelemetryPacketProps> = ({ packet }) => {
<EnvironmentMetricsPacket
packet={packet}
metrics={telemetry.environmentMetrics}
timestamp={telemetry.time}
timestamp={timestamp}
/>
);
}
@@ -58,8 +61,8 @@ export const TelemetryPacket: React.FC<TelemetryPacketProps> = ({ packet }) => {
>
<div className="text-neutral-400 text-sm">
Unknown telemetry data received at{' '}
{telemetry.time
? new Date(telemetry.time * 1000).toLocaleTimeString()
{timestamp
? new Date(timestamp * 1000).toLocaleTimeString()
: 'unknown time'
}
</div>

View File

@@ -306,6 +306,9 @@ export interface Data {
// Error tracking
decodeError?: string;
// Reception timestamp (added by decoder)
rxTime?: number;
}
// Packet represents a complete decoded MQTT message