CLI命令 ot channel 11 分析下面我们来分析命令 ot channel 11 在代码中是如何实现的。在文件 modules\lib\openthread\src\cli\cli.cpp 中我们可以找到这条命令的代码,它调用了函数 otLinkSetChannel 。如下图:
template <> otError Interpreter:

rocess<Cmd("channel")>(Arg aArgs[]){ otError error = OT_ERROR_NONE;else { ExitNow(error = ProcessGetSet(aArgs, otLinkGetChannel, otLinkSetChannel)); }exit: return error;}
我们找到函数 otLinkSetChannel 继续分析。下图并不是函数的全部代码,而是其中的部分关键代码。有兴趣地朋友可以去 modules\lib\openthread\src\core\api\link_api.cpp 中查看完整代码。otLinkSetChannel 里面最关键的一句是 SuccessOrExit(error = instance.Get<Mac::Mac>().SetPanChannel(aChannel))。 这句代码对不太了解C++的人很不友好。接下来我们会仔细地分析一下这句:
otError otLinkSetChannel(otInstance *aInstance, uint8_t aChannel){ Error error; Instance &instance AsCoreType(aInstance);#if OPENTHREAD CONFIG LINK RAW ENABLE if (instance.Get<Mac: LinkRaw>().IsEnabled()) { error instance. Get<Mac: LinkRaw>(). SetChannel(aChannel); ExitNow(); }#endif VerifyOrExit(instance. Get<Mle: MleRouter>(). IsDisabled(), error=kErrorInvalidState); SuccessOrExit(error instance. Get<Mac: Mac>(). SetPanChannel(aChannel)); instance. Get<MeshCoP: ActiveDatasetManager>(). Clear(); instance. Get<MeshCoP: PendingDatasetManager>(). Clear();exit: return error;}
先来分析 Instance &instance 这个定义。这里 instance 的数据类型是类 Instance。我们进一步分析类 Instance。从下面这张图中我们介绍两个 C++ 的知识。
- Namespace 命名空间。命名空间是一种用来避免命名冲突的机制,它可以将一段代码的名称隔离开,使其与其他代码的名称不冲突。下面这段代码中 Instance 是包含在 namespace ot 中的。
- Inheritance 继承。继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。下图中,类 Instance 是由基类 otInstance 和 基类 NonCopyable 派生而来。
- 继承 otInstance 的方式是 public。基类中的 public 成员继承后仍为public,protected 成员在派生类中为 protected,private 成员在派生类中不能使用。
- 继承 NonCopyable 的方式是 private。基类中的 public 成员继承后为 private ,protected 成员在派生类中为 private ,private 成员在派生类中不能使用。
namespace ot {/*** Represents an OpenThread instance.** Contains all the components used by OpenThread.**/class Instance public otInstance, private NonCopyable{public:/*** Represents the message buffer information (number of messages/buffers in all OT stack message queues).**/class BufferInfo public otBufferInfo, public Clearable<BufferInfo>{};
继续分析 instance.Get<Mac::Mac>().SetPanChannel(aChannel) 这一句中的 Get<Mac::Mac>()。我们在类 Instance 中可以找到这个函数的定义如下图。这里又有两个关于 C++ 的基础知识。
- Function Template 函数模板。建立一个通用的函数,其返回值类型,形参的类型都不确定指定而是采用虚拟类型来代替。通过将类型作为参数传递给模板,可以使编译器为该特定类型生成一个函数。因为模板允许您根据泛型类型而不是特定类型编程,所以这个过程有时被称为泛型编程。Get<Mac::Mac> 中< >里的类 Mac::Mac 就是告诉函数模板需要的返回值得类型。
- Reference 引用。引用不是新定义一个变量,而是给已存在变量取了一个别名。C++中用 & 来表示引用。因为 Get<Mac::Mac> 返回的是引用,所以后面是可以直接用点号来调用类成员。如果返回的指针,则需要用“->”来调用成员。所以 SetPanChannel(aChannel) 应该是类 Mac::Mac 的成员。
/*** Returns a reference to a given Type object belonging to the OpenThread instance.** For example, Get<MeshForwarder>()returns a reference to the MeshForwarder object of the instance.** Note that any Type for which the Get<Type> is defined MUST be uniquely accessible from the OpenThread* `Instance` through the member variable property hierarchy.** Specializations of the Get<Type>()method are defined in this file after the 'Instanceclass definition.** @returns A reference to the 'Type object of the instance.**/template <typename Type> inline Type &Get(void);template <> inline Mac::Mac &Instance::Get(void) { return mMac; }template <> inline Mac::SubMac &Istance::Get(void) { return mMac.mLinks.mSubMac; }
下面我们来看一下类 Mac::Mac。第一句我们就看到其中一个成员就是刚才我们分析过的类 Instance。但是这个类的名字是 ot::Instance,这是因为在 namespace 之外类 Instance 的全名就是ot::Instance。另外class ot::Instance 之前还有一个关键字 friend。
- friend 友元。友元(Friend)是C++中一种特殊的访问控制机制,它允许一个类或函数访问另一个类的私有成员。友元是一种权限授予机制,当一个类或函数被声明为另一个类的友元时,它就能够绕过访问权限进行访问,即使这些成员在普通情况下对外是不可见的。通过这种机制类 Mac 可以访问 类 Instance 的私有成员。
/*** Implements the IEEE 802.15.4 MAC.**/class Mac : public InstanceLocator, private NonCopyable{ friend class ot::Instance;public:
继续在类 Mac 中寻找,我们可以找到成员方法 SetPanChannel。
/** * Sets the IEEE 802.15.4 PAN channel. * * @param[in] aChannel The IEEE 802.15.4 PAN Channel. * * @retval kErrorNone Successfully set the IEEE 802.15.4 PAN channel. * @retval kErrorInvalidArgs The @p aChannel is not in the supported channel mask. **/Error SetPanChannel(uint8_t aChannel);
找到 SetPanChannel 的定义。里面调用了 Get<Notifier>().Update(mPanChannel, aChannel, kEventThreadChannelChanged) 我们去类 Notifier 找到成员 Update。最终 aChannel 的值传递给了变量 mRadioChannel 完成了对信道的设定。
Error Ma::SetPanchannel(uint8_t achannel){ Error error = kErrorNone; VerifyOrExit(mSupportedChannelMask.Contains(aChannel), error = kErrorInvalidArgs); SuccessOrExit(Get<Notifier>().Update(mPanChannel, aChannel, kEventThreadChannelChanged)); mCcaSuccessRateTracker.Clear(); VerifyOrExit(!mUsingTemporaryChannel); mRadioChannel = mPanChannel;#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE UpdateCsl();#endif UpdateIdleMode();exit: return error;}