mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
86b8bf6de4
This commit adds support for interacting with nodes via the CLI in the Nexus simulation framework. This enables writing higher-level integration tests that verify stack behavior and state through standard CLI commands. Key changes: - Integrated `Cli::Interpreter` into the `Nexus::Node` class. - Added `Node::InputCli()` to allow sending commands to a node with `printf`-style formatting. - Implemented output capturing logic in `Node::HandleCliOutput()` to buffer and parse CLI responses into individual lines, stored in a `CliOutputArray`. - Added helper methods to `CliOutputLine` for matching and validating the captured output. - Added a new `cli_basic` Nexus test to demonstrate and validate the CLI interaction functionality.
246 lines
9.4 KiB
C++
246 lines
9.4 KiB
C++
/*
|
|
* Copyright (c) 2024, 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.
|
|
*/
|
|
|
|
#ifndef OT_NEXUS_PLATFORM_NEXUS_NODE_HPP_
|
|
#define OT_NEXUS_PLATFORM_NEXUS_NODE_HPP_
|
|
|
|
#include "cli/cli.hpp"
|
|
#include "instance/instance.hpp"
|
|
|
|
#include "nexus_alarm.hpp"
|
|
#include "nexus_core.hpp"
|
|
#include "nexus_dns.hpp"
|
|
#include "nexus_infra_if.hpp"
|
|
#include "nexus_logging.hpp"
|
|
#include "nexus_mdns.hpp"
|
|
#include "nexus_radio.hpp"
|
|
#include "nexus_settings.hpp"
|
|
#include "nexus_trel.hpp"
|
|
#include "nexus_udp.hpp"
|
|
#include "nexus_utils.hpp"
|
|
|
|
namespace ot {
|
|
namespace Nexus {
|
|
|
|
class Platform
|
|
{
|
|
public:
|
|
Radio mRadio;
|
|
Alarm mAlarmMilli;
|
|
Alarm mAlarmMicro;
|
|
Logging mLogging;
|
|
Mdns mMdns;
|
|
UpstreamDns mUpstreamDns;
|
|
InfraIf mInfraIf;
|
|
Udp mUdp;
|
|
Settings mSettings;
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
Trel mTrel;
|
|
#endif
|
|
bool mPendingTasklet;
|
|
|
|
protected:
|
|
explicit Platform(Instance &aInstance)
|
|
: mUpstreamDns(aInstance)
|
|
, mInfraIf(aInstance)
|
|
, mUdp(aInstance)
|
|
, mPendingTasklet(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
class Node : public Platform, public Heap::Allocatable<Node>, public LinkedListEntry<Node>, public Instance
|
|
{
|
|
friend class Heap::Allocatable<Node>;
|
|
|
|
public:
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Helper methods - tests
|
|
|
|
enum JoinMode : uint8_t
|
|
{
|
|
kAsFtd,
|
|
kAsFed,
|
|
kAsMed,
|
|
kAsSed, // Request stable netdata (default for SED)
|
|
kAsSedWithFullNetData, // Request full netdata as SED
|
|
};
|
|
|
|
enum AddressNetif : uint8_t // Used in `const Ip6::Address &aAddress, AddressNetif aNetif)`
|
|
{
|
|
kThreadNetifAddress,
|
|
kInfraNetifAddress,
|
|
kAnyNetifAddress,
|
|
};
|
|
|
|
void Reset(void);
|
|
void Form(void);
|
|
void Join(Node &aNode, JoinMode aJoinMode = kAsFtd);
|
|
void AllowList(Node &aNode);
|
|
void UnallowList(Node &aNode);
|
|
void SendEchoRequest(const Ip6::Address &aDestination,
|
|
uint16_t aIdentifier = 0,
|
|
uint16_t aPayloadSize = 0,
|
|
uint8_t aHopLimit = 64,
|
|
const Ip6::Address *aSrcAddress = nullptr);
|
|
|
|
const Ip6::Address &FindMatchingAddress(const char *aPrefix);
|
|
const Ip6::Address &FindGlobalAddress(void);
|
|
|
|
bool Matches(const Ip6::Address &aAddress, AddressNetif aNetif) const;
|
|
bool Matches(uint32_t aId) const { return GetInstance().GetId() == aId; }
|
|
bool Matches(const Mac::ExtAddress &aExtAddress) const { return Get<Mac::Mac>().GetExtAddress() == aExtAddress; }
|
|
|
|
const char *GetExtendedRoleString(void) const;
|
|
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
void GetTrelSockAddr(Ip6::SockAddr &aSockAddr) const;
|
|
#endif
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Node Properties
|
|
|
|
uint32_t GetId(void) const { return GetInstance().GetId(); }
|
|
void SetName(const char *aName) { mName.Clear().Append("%s", aName); }
|
|
void SetName(const char *aPrefix, uint16_t aIndex);
|
|
const char *GetName(void) const { return mName.AsCString(); }
|
|
void SetPosition(float aX, float aY) { mX = aX, mY = aY; }
|
|
float GetPositionX(void) const { return mX; }
|
|
float GetPositionY(void) const { return mY; }
|
|
uint32_t GetLastParentId(void) const { return mLastParentId; }
|
|
void SetLastParentId(uint32_t aId) { mLastParentId = aId; }
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Helper methods - Access OpenThread Core components
|
|
|
|
template <typename Type> Type &Get(void) { return Instance::Get<Type>(); }
|
|
template <typename Type> const Type &Get(void) const { return AsConst(AsNonConst(this)->Get<Type>()); }
|
|
|
|
Instance &GetInstance(void) { return *this; }
|
|
const Instance &GetInstance(void) const { return *this; }
|
|
|
|
static Node &From(otInstance *aInstance)
|
|
{
|
|
Instance *instance = static_cast<Instance *>(aInstance);
|
|
return *static_cast<Node *>(instance);
|
|
}
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Helper methods - Callbacks
|
|
|
|
static void HandleIp6Receive(otMessage *aMessage, void *aContext);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// CLI
|
|
|
|
class CliOutputLine
|
|
{
|
|
friend class Node;
|
|
|
|
public:
|
|
CliOutputLine(void) = default;
|
|
CliOutputLine(const CliOutputLine &aOther) { SuccessOrQuit(mLine.Set(aOther.mLine)); }
|
|
CliOutputLine(CliOutputLine &&aOther) { mLine.TakeFrom(aOther.mLine.Move()); }
|
|
CliOutputLine &operator=(const CliOutputLine &aOther);
|
|
CliOutputLine &operator=(CliOutputLine &&aOther);
|
|
|
|
const char *GetLine(void) const { return mLine.AsCString(); }
|
|
|
|
bool Matches(const char *aLine) const { return StringMatch(GetLine(), aLine); }
|
|
bool StartsWith(const char *aSubString) const { return StringStartsWith(GetLine(), aSubString); }
|
|
bool EndsWith(const char *aSubString) const { return StringEndsWith(GetLine(), aSubString); }
|
|
bool IsDone(void) const { return Matches("Done"); }
|
|
|
|
private:
|
|
static constexpr uint16_t kMaxLineSize = 256;
|
|
|
|
Heap::String mLine;
|
|
};
|
|
|
|
typedef Heap::Array<CliOutputLine, 8> CliOutputArray;
|
|
|
|
void InputCli(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
|
|
void ClearCliOutput(void) { mCliOutputLines.Free(), mCliCurOutputLine.Clear(); }
|
|
const CliOutputArray &GetCliOutputLines(void) { return mCliOutputLines; }
|
|
bool IsCliOutputSuccess(void);
|
|
|
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// Platform components
|
|
|
|
using Platform::mAlarmMicro;
|
|
using Platform::mAlarmMilli;
|
|
using Platform::mInfraIf;
|
|
using Platform::mLogging;
|
|
using Platform::mMdns;
|
|
using Platform::mPendingTasklet;
|
|
using Platform::mRadio;
|
|
using Platform::mSettings;
|
|
using Platform::mUdp;
|
|
using Platform::mUpstreamDns;
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
using Platform::mTrel;
|
|
#endif
|
|
|
|
Node *mNext;
|
|
|
|
private:
|
|
Node(void);
|
|
void HandleIp6Receive(OwnedPtr<Message> aMessagePtr);
|
|
|
|
static int HandleCliOutput(void *aContext, const char *aFormat, va_list aArguments)
|
|
OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 0);
|
|
int HandleCliOutput(const char *aFormat, va_list aArguments) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 0);
|
|
|
|
CliOutputArray mCliOutputLines;
|
|
String<CliOutputLine::kMaxLineSize> mCliCurOutputLine;
|
|
Cli::Interpreter mCliInterpreter;
|
|
|
|
String<32> mName;
|
|
float mX;
|
|
float mY;
|
|
uint32_t mLastParentId;
|
|
};
|
|
|
|
inline Node &AsNode(otInstance *aInstance) { return Node::From(aInstance); }
|
|
|
|
void AllowLinkBetween(Node &aFirstNode, Node &aSecondNode);
|
|
void UnallowLinkBetween(Node &aFirstNode, Node &aSecondNode);
|
|
|
|
} // namespace Nexus
|
|
|
|
template <> inline Nexus::Node &Instance::Get(void) { return Nexus::AsNode(this); }
|
|
template <> inline Nexus::InfraIf &Instance::Get(void) { return static_cast<Nexus::Node *>(this)->mInfraIf; }
|
|
template <> inline Nexus::Udp &Instance::Get(void) { return static_cast<Nexus::Node *>(this)->mUdp; }
|
|
template <> inline Nexus::Trel &Instance::Get(void) { return static_cast<Nexus::Node *>(this)->mTrel; }
|
|
template <> inline Nexus::Mdns &Instance::Get(void) { return static_cast<Nexus::Node *>(this)->mMdns; }
|
|
template <> inline Nexus::UpstreamDns &Instance::Get(void) { return static_cast<Nexus::Node *>(this)->mUpstreamDns; }
|
|
|
|
} // namespace ot
|
|
|
|
#endif // OT_NEXUS_PLATFORM_NEXUS_NODE_HPP_
|