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