mirror of
https://github.com/espressif/openthread.git
synced 2026-10-08 08:47:38 +00:00
This commit refactors MAC frame parsing and field inspection across the OpenThread stack by making `Frame::ParseInfo`, `RxFrame::ParseInfo`, and `TxFrame::ParseInfo` public and deprecating/removing individual getter methods directly on `Frame`. Previously, `Frame` provided dozens of separate getter methods (`GetDstAddr()`, `GetSrcAddr()`, `GetDstPanId()`, `GetSrcPanId()`, `GetSecurityLevel()`, `GetKeyIdMode()`, `GetFrameCounter()`, `GetCommandId()`, `GetPayload()`, `Find<IeType>()`, etc.). Calling these methods repeatedly on the same frame resulted in multiple parsing passes over the PSDU buffer. Furthermore, callback handlers across `SubMac`, `Mac`, `MeshForwarder`, and various schedulers had to re-query individual fields or maintain partial state independently. This commit unifies frame parsing into a single-pass model: - `ParseInfo` extracts and caches all relevant frame header fields, addresses, PAN IDs, security attributes, Header IEs, and payload/header boundaries during a single parse pass. - Frame parsing and validation can be done incrementally using `ParseMode` (`kParseAddrFields`, `kParseSecurityHeader`, or `kParseFully`). - Passing `ParseInfo` across layer boundaries eliminates redundant parsing operations and simplifies code across the codebase.
600 lines
18 KiB
C++
600 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2026, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "nexus_grpc.hpp"
|
|
#include "nexus_sim.hpp"
|
|
|
|
#if OPENTHREAD_NEXUS_CONFIG_GRPC_ENABLE
|
|
|
|
#include <cerrno>
|
|
#include <chrono>
|
|
#include <cstring>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#include <grpcpp/grpcpp.h>
|
|
#include <unistd.h>
|
|
|
|
#include "nexus_logging.hpp"
|
|
#include "nexus_node.hpp"
|
|
#include "nexus_radio.hpp"
|
|
#include "nexus_radio_model.hpp"
|
|
#include "simulation.grpc.pb.h"
|
|
#include "common/clearable.hpp"
|
|
#include "common/code_utils.hpp"
|
|
#include "mac/mac_frame.hpp"
|
|
#include "thread/neighbor_table.hpp"
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace ot {
|
|
namespace Nexus {
|
|
|
|
void GrpcServer::InitEvent(nexus::SimulationEvent &aEvent) { aEvent.set_timestamp_us(Core::Get().GetNowMicro64()); }
|
|
|
|
nexus::NodeRole GrpcServer::GetEnhancedRole(Node *aNode)
|
|
{
|
|
const Mle::Mle &mle = aNode->Get<Mle::Mle>();
|
|
Mle::DeviceRole role = mle.GetRole();
|
|
|
|
switch (role)
|
|
{
|
|
case Mle::kRoleDisabled:
|
|
return nexus::ROLE_DISABLED;
|
|
case Mle::kRoleDetached:
|
|
return nexus::ROLE_DETACHED;
|
|
case Mle::kRoleLeader:
|
|
return nexus::ROLE_LEADER;
|
|
case Mle::kRoleRouter:
|
|
return nexus::ROLE_ROUTER;
|
|
case Mle::kRoleChild:
|
|
if (mle.IsFullThreadDevice())
|
|
{
|
|
return mle.IsRouterRoleAllowed() ? nexus::ROLE_REED : nexus::ROLE_FED;
|
|
}
|
|
|
|
return mle.IsRxOnWhenIdle() ? nexus::ROLE_MED : nexus::ROLE_SED;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nexus::ROLE_UNKNOWN;
|
|
}
|
|
|
|
class GrpcServer::ServiceImpl final : public nexus::NexusService::Service
|
|
{
|
|
public:
|
|
ServiceImpl(GrpcServer *aServer)
|
|
: mServerPtr(aServer)
|
|
{
|
|
}
|
|
|
|
grpc::Status StreamEvents(grpc::ServerContext *aContext,
|
|
const nexus::StreamRequest * /* aRequest */,
|
|
grpc::ServerWriter<nexus::SimulationEvent> *aWriter) override
|
|
{
|
|
static constexpr uint32_t kPopEventTimeoutMs = 1000;
|
|
grpc::Status status = grpc::Status::OK;
|
|
auto *queue = mServerPtr->AddEventQueue();
|
|
|
|
mServerPtr->ClientConnected();
|
|
aWriter->SendInitialMetadata();
|
|
mServerPtr->DumpState(queue);
|
|
|
|
while (!aContext->IsCancelled())
|
|
{
|
|
nexus::SimulationEvent event;
|
|
|
|
if (mServerPtr->PopEvent(queue, event, kPopEventTimeoutMs))
|
|
{
|
|
if (!aWriter->Write(event))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!mServerPtr->IsRunning())
|
|
{
|
|
break;
|
|
}
|
|
|
|
{
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
mServerPtr->InitEvent(event);
|
|
}
|
|
event.mutable_heartbeat();
|
|
|
|
if (!aWriter->Write(event))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mServerPtr->RemoveEventQueue(queue);
|
|
mServerPtr->ClientDisconnected();
|
|
return status;
|
|
}
|
|
|
|
grpc::Status Reset(grpc::ServerContext * /* aContext */,
|
|
const nexus::ResetRequest * /* aRequest */,
|
|
nexus::ResetResponse * /* aResponse */) override
|
|
{
|
|
Log("Reset requested. Signaling simulation loop to restart...");
|
|
|
|
Simulation::Get().RequestRestart();
|
|
|
|
return grpc::Status::OK;
|
|
}
|
|
|
|
grpc::Status SetSpeed(grpc::ServerContext * /* aContext */,
|
|
const nexus::SetSpeedRequest *aRequest,
|
|
nexus::SetSpeedResponse * /* aResponse */) override
|
|
{
|
|
float speedFactor = aRequest->speed_factor();
|
|
|
|
if (speedFactor < 0.0f)
|
|
{
|
|
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Speed factor cannot be negative");
|
|
}
|
|
|
|
{
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
Simulation::Get().SetSpeedFactor(speedFactor);
|
|
}
|
|
|
|
return grpc::Status::OK;
|
|
}
|
|
|
|
grpc::Status SetNodeState(grpc::ServerContext * /* aContext */,
|
|
const nexus::SetNodeStateRequest *aRequest,
|
|
nexus::SetNodeStateResponse * /* aResponse */) override
|
|
{
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
|
|
Log("SetNodeState: Node %d -> %s", aRequest->node_id(), aRequest->enabled() ? "ENABLE" : "DISABLE");
|
|
Core::Get().SetNodeEnabled(aRequest->node_id(), aRequest->enabled());
|
|
|
|
return grpc::Status::OK;
|
|
}
|
|
|
|
grpc::Status SetNodePosition(grpc::ServerContext * /* aContext */,
|
|
const nexus::SetNodePositionRequest *aRequest,
|
|
nexus::SetNodePositionResponse * /* aResponse */) override
|
|
{
|
|
grpc::Status status = grpc::Status::OK;
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
|
|
Node *node = Core::Get().FindNodeById(aRequest->node_id());
|
|
|
|
VerifyOrExit(node != nullptr, status = grpc::Status(grpc::StatusCode::NOT_FOUND, "Node not found"));
|
|
|
|
node->SetPosition(aRequest->x(), aRequest->y());
|
|
mServerPtr->OnNodeStateChanged(node);
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
grpc::Status CreateNode(grpc::ServerContext * /* aContext */,
|
|
const nexus::CreateNodeRequest *aRequest,
|
|
nexus::CreateNodeResponse *aResponse) override
|
|
{
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
|
|
Log("CreateNode: x=%f, y=%f", aRequest->x(), aRequest->y());
|
|
Node &node = Core::Get().CreateNode();
|
|
node.SetPosition(aRequest->x(), aRequest->y());
|
|
mServerPtr->OnNodeStateChanged(&node);
|
|
aResponse->set_node_id(node.GetId());
|
|
Log("CreateNode: done, id=%u", node.GetId());
|
|
|
|
return grpc::Status::OK;
|
|
}
|
|
|
|
grpc::Status FormNetwork(grpc::ServerContext * /* aContext */,
|
|
const nexus::FormNetworkRequest *aRequest,
|
|
nexus::FormNetworkResponse * /* aResponse */) override
|
|
{
|
|
grpc::Status status = grpc::Status::OK;
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
|
|
Node *node = Core::Get().FindNodeById(aRequest->node_id());
|
|
|
|
VerifyOrExit(node != nullptr, status = grpc::Status(grpc::StatusCode::NOT_FOUND, "Node not found"));
|
|
|
|
node->Form();
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
grpc::Status JoinNetwork(grpc::ServerContext * /* aContext */,
|
|
const nexus::JoinNetworkRequest *aRequest,
|
|
nexus::JoinNetworkResponse * /* aResponse */) override
|
|
{
|
|
grpc::Status status = grpc::Status::OK;
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
|
|
Node *joiner = Core::Get().FindNodeById(aRequest->node_id());
|
|
Node *target = Core::Get().FindNodeById(aRequest->target_node_id());
|
|
|
|
VerifyOrExit(joiner != nullptr && target != nullptr,
|
|
status = grpc::Status(grpc::StatusCode::NOT_FOUND, "Node not found"));
|
|
|
|
{
|
|
static constexpr Node::JoinMode kJoinModes[] = {
|
|
Node::kAsFtd, // JOIN_MODE_FTD
|
|
Node::kAsMed, // JOIN_MODE_MED
|
|
Node::kAsSed, // JOIN_MODE_SED
|
|
Node::kAsFed, // JOIN_MODE_FED
|
|
};
|
|
|
|
VerifyOrExit(static_cast<size_t>(aRequest->mode()) < GetArrayLength(kJoinModes),
|
|
status = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, "Invalid join mode"));
|
|
|
|
Node::JoinMode mode = kJoinModes[aRequest->mode()];
|
|
|
|
joiner->Join(*target, mode);
|
|
}
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
grpc::Status GetRadioParameters(grpc::ServerContext * /* aContext */,
|
|
const nexus::GetRadioParametersRequest * /* aRequest */,
|
|
nexus::GetRadioParametersResponse *aResponse) override
|
|
{
|
|
aResponse->set_path_loss_constant(RadioModel::kPathLossConstant);
|
|
aResponse->set_path_loss_exponent(RadioModel::kPathLossExponent);
|
|
aResponse->set_radio_sensitivity(Radio::kRadioSensitivity);
|
|
aResponse->set_mle_link_request_margin_min(OPENTHREAD_CONFIG_MLE_LINK_REQUEST_MARGIN_MIN);
|
|
|
|
return grpc::Status::OK;
|
|
}
|
|
|
|
private:
|
|
GrpcServer *mServerPtr;
|
|
};
|
|
|
|
GrpcServer::GrpcServer(const char *aExecutablePath)
|
|
: mRunning(true)
|
|
, mClientCount(0)
|
|
, mExecutablePath(aExecutablePath != nullptr ? aExecutablePath : "")
|
|
{
|
|
mService = std::make_unique<ServiceImpl>(this);
|
|
|
|
grpc::ServerBuilder builder;
|
|
builder.AddListeningPort(kDefaultGrpcServerAddress, grpc::InsecureServerCredentials());
|
|
builder.RegisterService(mService.get());
|
|
|
|
mServer = builder.BuildAndStart();
|
|
|
|
if (mServer)
|
|
{
|
|
Log("gRPC Server started at %s", kDefaultGrpcServerAddress);
|
|
mThread = std::thread([this]() { mServer->Wait(); });
|
|
{
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
Core::Get().AddObserver(*this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log("Failed to start gRPC server!");
|
|
}
|
|
}
|
|
|
|
GrpcServer::~GrpcServer()
|
|
{
|
|
{
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
Core::Get().RemoveObserver(*this);
|
|
}
|
|
mRunning = false;
|
|
mCv.notify_all();
|
|
if (mServer)
|
|
{
|
|
mServer->Shutdown();
|
|
}
|
|
if (mThread.joinable())
|
|
{
|
|
mThread.join();
|
|
}
|
|
}
|
|
|
|
void GrpcServer::ClientConnected(void)
|
|
{
|
|
mClientCount++;
|
|
Log("UI connected!");
|
|
}
|
|
|
|
void GrpcServer::ClientDisconnected(void)
|
|
{
|
|
if (mClientCount.fetch_sub(1) == 1)
|
|
{
|
|
Log("All UI clients disconnected.");
|
|
}
|
|
}
|
|
|
|
void GrpcServer::PushEvent(const nexus::SimulationEvent &aEvent, std::queue<nexus::SimulationEvent> *aTargetQueue)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
if (aTargetQueue != nullptr)
|
|
{
|
|
if (aTargetQueue->size() >= kMaxQueueSize)
|
|
{
|
|
aTargetQueue->pop();
|
|
}
|
|
aTargetQueue->push(aEvent);
|
|
}
|
|
else
|
|
{
|
|
for (auto &queue : mClientQueues)
|
|
{
|
|
if (queue.size() >= kMaxQueueSize)
|
|
{
|
|
queue.pop();
|
|
}
|
|
queue.push(aEvent);
|
|
}
|
|
}
|
|
mCv.notify_all();
|
|
}
|
|
|
|
bool GrpcServer::PopEvent(std::queue<nexus::SimulationEvent> *aQueue,
|
|
nexus::SimulationEvent &aEvent,
|
|
uint32_t aTimeoutMs)
|
|
{
|
|
bool found = false;
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
|
|
VerifyOrExit(mCv.wait_for(lock, std::chrono::milliseconds(aTimeoutMs),
|
|
[this, aQueue] { return !aQueue->empty() || !mRunning; }));
|
|
|
|
VerifyOrExit(mRunning || !aQueue->empty());
|
|
|
|
aEvent = aQueue->front();
|
|
aQueue->pop();
|
|
found = true;
|
|
|
|
exit:
|
|
return found;
|
|
}
|
|
|
|
std::queue<nexus::SimulationEvent> *GrpcServer::AddEventQueue(void)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
mClientQueues.emplace_back();
|
|
return &mClientQueues.back();
|
|
}
|
|
|
|
void GrpcServer::RemoveEventQueue(std::queue<nexus::SimulationEvent> *aQueue)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
for (auto it = mClientQueues.begin(); it != mClientQueues.end(); ++it)
|
|
{
|
|
if (&(*it) == aQueue)
|
|
{
|
|
mClientQueues.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GrpcServer::ClearQueue(void)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
for (auto &queue : mClientQueues)
|
|
{
|
|
std::queue<nexus::SimulationEvent> empty;
|
|
std::swap(queue, empty);
|
|
}
|
|
}
|
|
|
|
GrpcServer::PacketInfo GrpcServer::GetPacketInfo(const Mac::Frame &aFrame)
|
|
{
|
|
PacketInfo info;
|
|
Mac::Frame::ParseInfo frameInfo;
|
|
|
|
IgnoreError(frameInfo.ParseFrom(aFrame, Mac::Frame::kParseFully));
|
|
|
|
if (frameInfo.mType == Mac::Frame::kTypeAck)
|
|
{
|
|
info.mProtocol = "IEEE 802.15.4 ACK";
|
|
info.mSummary = "ACK";
|
|
}
|
|
else if (frameInfo.mType == Mac::Frame::kTypeData)
|
|
{
|
|
info.mProtocol = "IEEE 802.15.4 Data";
|
|
info.mSummary = "Data";
|
|
}
|
|
else if (frameInfo.mType == Mac::Frame::kTypeMacCmd)
|
|
{
|
|
info.mProtocol = "IEEE 802.15.4 Command";
|
|
info.mSummary = "Command";
|
|
}
|
|
else
|
|
{
|
|
info.mProtocol = "IEEE 802.15.4";
|
|
info.mSummary = "Other";
|
|
}
|
|
|
|
if (frameInfo.mIsSeqNumPresent)
|
|
{
|
|
char buf[32];
|
|
|
|
snprintf(buf, sizeof(buf), " (seq=%u)", frameInfo.mSequenceNum);
|
|
info.mSummary += buf;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
void GrpcServer::PushPacketEvent(uint32_t aSenderId,
|
|
uint32_t aDestinationId,
|
|
const uint8_t *aData,
|
|
uint16_t aLen,
|
|
std::queue<nexus::SimulationEvent> *aTargetQueue)
|
|
{
|
|
nexus::SimulationEvent event;
|
|
otRadioFrame radioFrame;
|
|
|
|
InitEvent(event);
|
|
|
|
auto *packetEvent = event.mutable_packet_captured();
|
|
packetEvent->set_source_node_id(aSenderId);
|
|
packetEvent->set_destination_node_id(aDestinationId);
|
|
packetEvent->set_raw_payload(aData, aLen);
|
|
|
|
ClearAllBytes(radioFrame);
|
|
radioFrame.mPsdu = const_cast<uint8_t *>(aData);
|
|
radioFrame.mLength = aLen;
|
|
|
|
{
|
|
const Mac::Frame &frame = static_cast<const Mac::Frame &>(radioFrame);
|
|
PacketInfo info = GetPacketInfo(frame);
|
|
|
|
packetEvent->set_protocol(info.mProtocol);
|
|
packetEvent->set_summary(info.mSummary);
|
|
}
|
|
|
|
PushEvent(event, aTargetQueue);
|
|
}
|
|
|
|
void GrpcServer::PushNodeEvent(Node *aNode, std::queue<nexus::SimulationEvent> *aTargetQueue)
|
|
{
|
|
nexus::SimulationEvent event;
|
|
InitEvent(event);
|
|
|
|
auto *nodeEvent = event.mutable_node_update();
|
|
nodeEvent->set_node_id(aNode->GetId());
|
|
nodeEvent->set_role(GetEnhancedRole(aNode));
|
|
nodeEvent->set_rloc16(aNode->Get<Mle::Mle>().GetRloc16());
|
|
nodeEvent->set_x(aNode->GetPositionX());
|
|
nodeEvent->set_y(aNode->GetPositionY());
|
|
|
|
Log("PushNodeEvent: id=%u, role=%d, x=%f, y=%f", nodeEvent->node_id(), static_cast<int>(nodeEvent->role()),
|
|
nodeEvent->x(), nodeEvent->y());
|
|
|
|
PushEvent(event, aTargetQueue);
|
|
}
|
|
|
|
void GrpcServer::PushLinkUpdate(uint32_t aSrcId,
|
|
uint32_t aDstId,
|
|
bool aIsActive,
|
|
std::queue<nexus::SimulationEvent> *aTargetQueue)
|
|
{
|
|
Log("PushLinkUpdate: src=%u, dst=%u, active=%d", aSrcId, aDstId, aIsActive);
|
|
|
|
nexus::SimulationEvent event;
|
|
InitEvent(event);
|
|
|
|
auto *linkEvent = event.mutable_link_update();
|
|
linkEvent->set_source_node_id(aSrcId);
|
|
linkEvent->set_destination_node_id(aDstId);
|
|
linkEvent->set_is_active(aIsActive);
|
|
|
|
PushEvent(event, aTargetQueue);
|
|
}
|
|
|
|
void GrpcServer::OnHeartbeat(uint64_t aTimestampUs)
|
|
{
|
|
nexus::SimulationEvent event;
|
|
event.set_timestamp_us(aTimestampUs);
|
|
event.mutable_heartbeat();
|
|
PushEvent(event);
|
|
}
|
|
|
|
void GrpcServer::DumpState(std::queue<nexus::SimulationEvent> *aQueue)
|
|
{
|
|
std::lock_guard<Simulation> lock(Simulation::Get());
|
|
|
|
{
|
|
nexus::SimulationEvent event;
|
|
event.set_timestamp_us(Core::Get().GetNowMicro64());
|
|
event.mutable_heartbeat();
|
|
PushEvent(event, aQueue);
|
|
}
|
|
|
|
Log("DumpState: Starting sync...");
|
|
|
|
// 1. Sync Nodes
|
|
for (Node &node : Core::Get().GetNodes())
|
|
{
|
|
PushNodeEvent(&node, aQueue);
|
|
}
|
|
|
|
// 2. Sync Links
|
|
for (Node &node : Core::Get().GetNodes())
|
|
{
|
|
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
|
|
Neighbor::Info neighborInfo;
|
|
|
|
while (node.Get<NeighborTable>().GetNextNeighborInfo(iterator, neighborInfo) == kErrorNone)
|
|
{
|
|
Node *neighborNode =
|
|
Core::Get().GetNodes().FindMatching(static_cast<const Mac::ExtAddress &>(neighborInfo.mExtAddress));
|
|
|
|
if (neighborNode != nullptr)
|
|
{
|
|
PushLinkUpdate(node.GetId(), neighborNode->GetId(), true, aQueue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GrpcServer::OnNodeStateChanged(Node *aNode) { PushNodeEvent(aNode); }
|
|
|
|
void GrpcServer::OnLinkUpdate(uint32_t aSrcId, uint32_t aDstId, bool aIsActive)
|
|
{
|
|
PushLinkUpdate(aSrcId, aDstId, aIsActive);
|
|
}
|
|
|
|
void GrpcServer::OnPacketEvent(uint32_t aSenderId, uint32_t aDestinationId, const uint8_t *aData, uint16_t aLen)
|
|
{
|
|
PushPacketEvent(aSenderId, aDestinationId, aData, aLen);
|
|
}
|
|
|
|
void GrpcServer::OnClearEvents(void) { ClearQueue(); }
|
|
|
|
bool GrpcServer::IsConnected(void) const { return mClientCount > 0; }
|
|
|
|
} // namespace Nexus
|
|
} // namespace ot
|
|
|
|
#endif // OPENTHREAD_NEXUS_CONFIG_GRPC_ENABLE
|