feat(quic): add robot ID to registration and heartbeat
This commit is contained in:
parent
f57807372d
commit
ba8f1e1821
@ -7,6 +7,7 @@ quic_edge {
|
||||
server_port: 4433
|
||||
alpn: "cmvr-quic-edge/1"
|
||||
node_id: "cmvr-edge"
|
||||
robot_id: "CN-CMVR-MBLRV1-CHAGAN-20260731-001"
|
||||
software_version: "0.1"
|
||||
|
||||
# The existing cmvr-es gRPC server remains the robot-control endpoint. "auto"
|
||||
|
||||
@ -217,6 +217,7 @@ void populateDescriptor(const config::QuicEdgeConfig& config,
|
||||
{
|
||||
if (!descriptor) return;
|
||||
descriptor->set_node_id(node_id);
|
||||
descriptor->set_robot_id(config.robot_id());
|
||||
descriptor->set_boot_id(boot_id);
|
||||
descriptor->set_software_version(software_version);
|
||||
const auto interfaces =
|
||||
@ -442,6 +443,10 @@ bool QuicEdgeService::validateConfig(const config::QuicEdgeConfig& config,
|
||||
setError(error, "QUIC edge task id is empty");
|
||||
return false;
|
||||
}
|
||||
if (config.robot_id().empty()) {
|
||||
setError(error, "QUIC edge robot_id is empty");
|
||||
return false;
|
||||
}
|
||||
if (config.server_host().empty()) {
|
||||
setError(error, "QUIC edge server_host is empty");
|
||||
return false;
|
||||
@ -1097,6 +1102,7 @@ bool QuicEdgeService::sendNodeRegistration(std::string* error)
|
||||
<< ", message_sequence=" << envelope.message_sequence()
|
||||
<< ", payload_bytes=" << serialized.size()
|
||||
<< ", node_id=" << node.node_id()
|
||||
<< ", robot_id=" << node.robot_id()
|
||||
<< ", boot_id=" << node.boot_id()
|
||||
<< ", grpc_endpoint=" << node.grpc_endpoint().host()
|
||||
<< ':' << node.grpc_endpoint().port()
|
||||
@ -1145,6 +1151,7 @@ bool QuicEdgeService::sendHeartbeat(const std::uint64_t sequence,
|
||||
envelope.set_message_sequence(control_message_sequence_++);
|
||||
auto* heartbeat = envelope.mutable_node_heartbeat();
|
||||
heartbeat->set_node_id(node_id_);
|
||||
heartbeat->set_robot_id(config_.robot_id());
|
||||
heartbeat->set_boot_id(boot_id_);
|
||||
heartbeat->set_session_id(session_id);
|
||||
heartbeat->set_sequence(sequence);
|
||||
|
||||
@ -105,6 +105,7 @@ public:
|
||||
if (envelope.has_node_register_request()) {
|
||||
const auto& request = envelope.node_register_request();
|
||||
last_registered_node_id_ = request.node().node_id();
|
||||
last_registered_robot_id_ = request.node().robot_id();
|
||||
last_grpc_endpoint_port_ = request.node().grpc_endpoint().port();
|
||||
last_interface_count_ = request.node().local_interfaces_size();
|
||||
cmvr::quic_edge::v1::EdgeControlEnvelope response;
|
||||
@ -253,6 +254,12 @@ public:
|
||||
return last_registered_node_id_;
|
||||
}
|
||||
|
||||
std::string lastRegisteredRobotId() const
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
return last_registered_robot_id_;
|
||||
}
|
||||
|
||||
std::uint32_t lastGrpcEndpointPort() const
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
@ -335,6 +342,7 @@ private:
|
||||
std::uint32_t heartbeat_ack_delay_ms_{0U};
|
||||
std::uint64_t server_message_sequence_{0};
|
||||
std::string last_registered_node_id_;
|
||||
std::string last_registered_robot_id_;
|
||||
std::uint32_t last_grpc_endpoint_port_{0};
|
||||
int last_interface_count_{0};
|
||||
std::vector<std::vector<std::uint8_t>> controls_;
|
||||
@ -359,6 +367,7 @@ config::QuicEdgeConfig validConfig(const std::string& source_track_id)
|
||||
config.set_server_port(4433);
|
||||
config.set_alpn("cmvr-quic-edge/1");
|
||||
config.set_node_id("test-node");
|
||||
config.set_robot_id("CN-CMVR-MBLRV1-CHAGAN-20260731-001");
|
||||
config.set_software_version("test-version");
|
||||
config.set_grpc_endpoint_host("auto");
|
||||
config.set_grpc_endpoint_port(50052U);
|
||||
@ -507,6 +516,8 @@ bool testServiceWithSharedHub()
|
||||
service.stats().heartbeats_acknowledged >= 1U;
|
||||
}));
|
||||
CHECK_TRUE(transport_view->lastRegisteredNodeId() == "test-node");
|
||||
CHECK_TRUE(transport_view->lastRegisteredRobotId() ==
|
||||
"CN-CMVR-MBLRV1-CHAGAN-20260731-001");
|
||||
CHECK_TRUE(transport_view->lastGrpcEndpointPort() == 50052U);
|
||||
CHECK_TRUE(transport_view->connectCount() == 1U);
|
||||
|
||||
@ -664,6 +675,8 @@ bool testDeviceManagerSnapshotInHeartbeat()
|
||||
const auto heartbeat = transport_view->lastHeartbeat();
|
||||
service.stop();
|
||||
|
||||
CHECK_TRUE(heartbeat.robot_id() ==
|
||||
"CN-CMVR-MBLRV1-CHAGAN-20260731-001");
|
||||
CHECK_TRUE(heartbeat.has_device_manager());
|
||||
CHECK_TRUE(heartbeat.device_manager().manager_name() ==
|
||||
"edge-device-manager");
|
||||
@ -1016,6 +1029,20 @@ bool testLifecycleStateGuards()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool testRobotIdIsRequired()
|
||||
{
|
||||
media::MediaSourceHub hub;
|
||||
auto config = validPresenceOnlyConfig();
|
||||
config.clear_robot_id();
|
||||
auto transport = std::make_unique<FakeTransport>();
|
||||
quic_edge::QuicEdgeService service(
|
||||
std::move(config), std::move(transport), hub);
|
||||
std::string error;
|
||||
CHECK_TRUE(!service.initialize(&error));
|
||||
CHECK_TRUE(error.find("robot_id") != std::string::npos);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
@ -1031,7 +1058,7 @@ int main()
|
||||
!testRegistrationRejectionBacksOff() ||
|
||||
!testHeartbeatAckRequiresSessionId() ||
|
||||
!testSlowMediaStartDoesNotBlockHeartbeat() ||
|
||||
!testLifecycleStateGuards()) {
|
||||
!testLifecycleStateGuards() || !testRobotIdIsRequired()) {
|
||||
return 1;
|
||||
}
|
||||
std::cout << "quic_edge_protocol_test: PASS\n";
|
||||
|
||||
@ -83,6 +83,10 @@ message QuicEdgeConfig {
|
||||
// IP reporting and heartbeat continue without a camera or microphone.
|
||||
repeated QuicEdgeTrackConfig tracks = 20;
|
||||
bool include_loopback_interfaces = 21;
|
||||
|
||||
// Immutable robot product identity, for example:
|
||||
// CN-CMVR-MBLRV1-CHAGAN-20260731-001.
|
||||
string robot_id = 22;
|
||||
}
|
||||
|
||||
message QuicEdgeRootConfig {
|
||||
|
||||
@ -121,8 +121,9 @@ explicit.
|
||||
The legal session order is:
|
||||
|
||||
1. The edge sends `NodeRegisterRequest` as its first application message after
|
||||
every QUIC connect or reconnect. It includes `node_id`, `boot_id`, software
|
||||
version, the current IPv4/IPv6 interface snapshot and advertised gRPC endpoint.
|
||||
every QUIC connect or reconnect. It includes `node_id`, the immutable
|
||||
`robot_id`, `boot_id`, software version, the current IPv4/IPv6 interface
|
||||
snapshot and advertised gRPC endpoint.
|
||||
2. The gateway replies with `NodeRegisterResponse`. Media and heartbeat must not
|
||||
start until `accepted=true` and a non-empty `session_id` are received.
|
||||
3. The edge sends `NodeHeartbeat` at the negotiated interval. The gateway returns
|
||||
@ -189,12 +190,13 @@ registered QUIC connection. The edge clamps the negotiated value to its
|
||||
supported safety range and returns to the local value on reconnect until a new
|
||||
registration response is accepted.
|
||||
|
||||
`device_manager = 9` is an additive protobuf field in `NodeHeartbeat`, so this
|
||||
extension remains QUIC edge protocol v1. Existing gateways ignore the unknown
|
||||
field. Updated gateways must continue accepting older v1 heartbeats where
|
||||
`device_manager` is absent and must not treat an absent snapshot as an empty,
|
||||
healthy DeviceManager. Enum values may only be appended; existing numeric
|
||||
meanings must never be renumbered or reused.
|
||||
`NodeDescriptor.robot_id = 6`, `NodeHeartbeat.device_manager = 9` and
|
||||
`NodeHeartbeat.robot_id = 10` are additive protobuf fields, so these extensions
|
||||
remain QUIC edge protocol v1. Existing gateways ignore unknown fields. Updated
|
||||
gateways must continue accepting older v1 messages where these fields are absent
|
||||
and must not treat an absent device snapshot as an empty, healthy DeviceManager.
|
||||
Enum values may only be appended; existing numeric meanings must never be
|
||||
renumbered or reused.
|
||||
|
||||
DATAGRAM negotiation is required only when at least one media track is enabled.
|
||||
The reliable registration and heartbeat path remains valid for a zero-track
|
||||
|
||||
@ -128,6 +128,7 @@ message NodeDescriptor {
|
||||
string software_version = 3;
|
||||
repeated NetworkInterfaceAddress local_interfaces = 4;
|
||||
GrpcEndpoint grpc_endpoint = 5;
|
||||
string robot_id = 6;
|
||||
}
|
||||
|
||||
// This must be the first application message sent after each QUIC connection
|
||||
@ -162,6 +163,7 @@ message NodeHeartbeat {
|
||||
repeated NetworkInterfaceAddress local_interfaces = 7;
|
||||
GrpcEndpoint grpc_endpoint = 8;
|
||||
DeviceManagerSnapshot device_manager = 9;
|
||||
string robot_id = 10;
|
||||
}
|
||||
|
||||
message NodeHeartbeatAck {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user