我们先来看看Otcl脚本:
set val(chan) Channel/WirelessChannel ;#无线信道类型
set val(prop) Propagation/TwoRayGround ;#无线传输模型
set val(netif) Phy/WirelessPhy ;#网络接口类型
set val(mac) Mac/802_11 ;#MAC层协议
set val(ifq) Queue/DropTail/PriQueue ;#队列接口类型
#set val(ifq) CMUPriQueue ;#队列接口类型,DSR协议
set val(ll) LL ;#逻辑链路层类型
set val(ant) Antenna/OmniAntenna ;#天线模型
set val(adhocRouting) AODV ;#无线路由协议
••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
#配置无线节点
$ns_ node-config -adhocRouting $val(adhocRouting) \
-llType $val(ll) \
-macType $val(mac) \
-ifqType $val(ifq) \
-ifqLen $val(ifqlen) \
••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
上面的代码描述了无线节点会通过AODV路由协议来进行配置,现在看看AODV路由协议是怎么执行的。在aodv.cc中:
static class AODVclass : public TclClass {
public:
AODVclass() : TclClass("Agent/AODV") {} //构造函数调用其基类的构造函数
TclObject* create(int argc, const char*const* argv) {
assert(argc == 5);
//return (new AODV((nsaddr_t) atoi(argv[4])));
//返回一个新建的编译类的实例对象
return (new AODV((nsaddr_t) Address::instance().str2addr(argv[4])));
}
} class_rtProtoAODV;
类AODVclass实际上只有两个函数:构造函数和creat()函数。构造函数AODVclass()实际上调用了其父类TclClass的构造函数,并且传递了Agent/AODV作为参数。接下来看看TclClass是如何工作的:
TclClass::TclClass(const char* classname) : class_(0), classname_(classname)
{
if (Tcl::instance().interp()!=NULL) {//如果解释器存在
bind();
} else {
next_ = all_;
all_ = this;
}
}
下面看看bind()函数是如何执行的:
void TclClass::bind()
{
Tcl& tcl = Tcl::instance();
//在Otcl环境中注册类名:Agent/AODV。其父类是SplitObject
tcl.evalf("SplitObject register %s", classname_);
class_ = OTclGetClass(tcl.interp(), (char*)classname_);
//这两行代码实际上是为这个类创建了create-shadow和delete-shadow这两个命令
// create-shadow是创建OTcl影像类的命令
OTclAddIMethod(class_, "create-shadow",
create_shadow, (ClientData)this, 0);
OTclAddIMethod(class_, "delete-shadow",
delete_shadow, (ClientData)this, 0);
otcl_mappings();
}
此时我们已经在OTcl环境中注册了OTcl类:Agent/AODV。但是对应于Otcl解释类的编译类还没有被构造,并且也没有被关联。
当我们在OTcl脚本中使用AODV配置节点时,会创建一个OTcl解释类对象实例,这就会调用tclcl-1.15\tcl-object.tcl中的new过程
proc new { className args } {
set o [SplitObject getid]
if [catch "$className create $o $args" msg] {//调用了该类的creat函数
if [string match "__FAILED_SHADOW_OBJECT_" $msg] {
#
# The shadow object failed to be allocated.
#
delete $o
return ""
}
global errorInfo
error "class $className: constructor failed: $msg" $errorInfo
}
return $o
}
上面显示的调用了该类的creat函数,实际上是调用了Agent/AODV:creat(),实际上也是调用了其父类SplitObject的creat()函数,SplitObject实际上没有creat()函数,它调用了Class的create()函数
Class instproc create {obj args} {
set h [$self info heritage]
foreach i [concat $self $h] {
if {[$i info commands alloc] != {}} then {
set args [eval [list $i] alloc [list $obj] $args]
$obj class $self
eval [list $obj] init $args ;#调用了init过程
return $obj
}
}
error {No reachable alloc}
}
这实际上又调用了SplitObject的init()函数
SplitObject instproc init args {
$self next
if [catch "$self create-shadow $args"] {
error "__FAILED_SHADOW_OBJECT_" ""
}
}
这实际上调用了Agent/AODV 的create-shadow()函数
int TclClass::create_shadow(ClientData clientData, Tcl_Interp *interp,
int argc, CONST84 char *argv[])
{
TclClass* p = (TclClass*)clientData;
TclObject* o = p->create(argc, argv);
Tcl& tcl = Tcl::instance();
if (o != 0) {
o->name(argv[0]);
tcl.enter(o);
if (o->init(argc - 2, argv + 2) == TCL_ERROR) {
tcl.remove(o);
delete o;
return (TCL_ERROR);
}
tcl.result(o->name());
//在这里添加了两个命令:cmd和instvar。
OTclAddPMethod(OTclGetObject(interp, argv[0]), "cmd",
dispatch_cmd, (ClientData)o, 0);
OTclAddPMethod(OTclGetObject(interp, argv[0]), "instvar",
dispatch_instvar, (ClientData)o, 0);
o->delay_bind_init_all();
return (TCL_OK);
} else {
tcl.resultf("new failed while creating object of class %s",
p->classname_);
return (TCL_ERROR);
}
}
cmd()命令激活影像对象的command()方法,并将cmd()的参数以向量形式传递给command()方法,因此此函数必须实现,其用途就是用来接收cmd()传递的OTcl命令。
原文链接: https://www.cnblogs.com/yue-/archive/2012/06/10/6260054.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/52415
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!