forked from iarv/meshstream
Track rx time
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -91,4 +91,7 @@ message Data {
|
||||
|
||||
// Error tracking
|
||||
string decode_error = 60;
|
||||
|
||||
// Reception timestamp (added by decoder)
|
||||
uint64 rx_time = 61;
|
||||
}
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user