我们先来看看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命令。