The ONE是基于代理离散事件(agent-based discrete event)仿真器。把节点间连接建立UP或DOWN,消息产生、发送、接收都视为事件,The ONE进行一些初始化工作后,反反复复处理这些动态事件,这就是The ONE运行原理,这点非常类似于Contiki(机制是相通的)。因此,理清The ONE的main函数和事件处理,就能对The ONE有个大局观,日后编程,思路也更加清晰。本文深入源码剖析The ONE的主函数。
1. main概述
The ONE有两种运行模式,即TEXT和GUI,以TEXT为例,命令行运行方式如下:
./one.sh –b 5 default_settings.txt
以批处理(batch mode)为例,main函数整个调用过程如下:
main(String[] args) //DTNSim.java initSettings(confFiles, firstConfIndex); //初始化设置,读入default_settings.txt new DTNSimTextUI().start(); initModel(); //创建仿真模型 runSim(); //开始仿真 world.update(); //在每个updateInterval,处理所有到期事件
首先,解析运行命令,将运行次数5保存在nrofRuns
;initSettings()
将配置文件default_settings.txt
读到static Properties props
。在这里,直观理解成将txt文件读到内存,方便后续读取。
其次,initModel()
创建仿真模型。先基于配置文件创建场景(SimScenario.getInstance
,创建DTNHost
,设置World
);增加reports
,处理warmupTime
(clockTime -= warmupTime
, warmupTime
在配置文件设置),对移动模型热身warmupMovementModel()
。
最后,rumSim()
开始仿真。执行更新world.update()
,努力(在updateInterval
内)处理所有事件,更新节点移动moveHosts
,更新节点连接情况updateHosts
。
2. 相关源代码
The ONE有两种运行模式,即TEXT和GUI,以TEXT为例,命令行运行方式如下:
./one.sh –b 5 default_settings.txt
-b指批处理模式(batch mode),5指运行次数(nrofRuns[2]
,实际上是一个范围),default_settings.txt
是配置文件。
2.1 main函数
main
函数首先解析命令行参数,读入初始设置文件default_settings.txt
;接着创建仿真实例,创建仿真模型;最后开始仿真。去掉一些不相关的代码,main
函数源代码(core/DTNSim.java
)如下:
public static void main(String[] args) { nrofRuns = parseNrofRuns(args[1]); //解析命令行参数,将运行次数保存在nrofRuns[2],配置文件名保存在confFiles[] initSettings(confFiles, firstConfIndex); //confFiles在这里是default_settings.txt for (int i=nrofRuns[0]; i<nrofRuns[1]; i++) { Settings.setRunIndex(i); resetForNextRun(); new DTNSimTextUI().start(); //TextUI } }
new DTNSimTextUI()
得到一个场景实例,调用start()
,该函数仅包含调用另两个函数:initModel()
和runSim()
。源代码如下:
//DTNSim.java中new DTNSimTextUI().start(); //DTNSimTextUI.java public void start() { initModel(); //增加reports, 设置仿真时间(减去warmupTime) runSim(); //抽象函数,定位到DTNSimTextUI.runSim() }
2.2 初始化模型initModel
初始化模型initModel
主要做了两件事:其一,增加报告reports
;其二,处理热身时间warmup
。相关源代码如下:
//DTNSimUI.java 初始化模型 protected SimScenario scen; private void initModel() { settings = new Settings(); this.scen = SimScenario.getInstance(); //创建场景,包括创建DTNHost /*** 增加报告, add reports ***/ protected Vector<Report> reports; // String reportClass = settings.getSetting(REPORT_S + i); //REPORT_S="Report.report",想想配置文件 addReport((Report)settings.createObject(REPORT_PAC + reportClass)); //REPORT_PAC="report.", 包名前缀 /*** 处理热身,即不把热身这段时间的仿真统计信息计算在内 ***/ SimClock c = SimClock.getInstance(); c.setTime(-warmupTime); //移动模型热身 protected World world; //包含所有节点,更新节点的位置和连接状态 this.world = this.scen.getWorld(); world.warmupMovementModel(warmupTime); }
2.3 开始仿真rumSim
rumSim
最主要的是调用world.update()
,其主要源代码如下:
//DTNSimTextUI.java protected void runSim() { double simTime = SimClock.getTime(); double endTime = scen.getEndTime(); while (simTime < endTime && !simCancelled) { world.update(); simTime = SimClock.getTime(); this.update(false); //如果下一个updateInterval还没到,就什么都不做 } /*** 整个仿真结束 ***/ simDone = true; done(); //所有Report r.close() this.update(true); //更新时间统计值 }
2.4 world.update
world.update()
处理所有到期的事件。获取含有到期事件的事件队列,处理到期的事件。主要源代码如下:
//World.java private List<EventQueue> eventQueues; //事件队列链表,用链表将事件队列组织起来 public void update () { double runUntil = SimClock.getTime() + this.updateInterval; // /* process all events that are due until next interval update */ setNextEventQueue(); //找到一个事件队列,该事件队列含有本updateInterval可以处理的事件 while (this.nextQueueEventTime <= runUntil) { simClock.setTime(this.nextQueueEventTime); ExternalEvent ee = this.nextEventQueue.nextEvent(); //取得事件 ee.processEvent(this); //处理事件 updateHosts(); // update all hosts after every event setNextEventQueue(); } moveHosts(this.updateInterval); //Moves all hosts in the world for a given amount of time simClock.setTime(runUntil); updateHosts(); }赞赏
微信赞赏
支付宝赞赏
楼主,假如有多个配置文件,initSettings()函数是怎么加载的?多个配置文件不会产生冲突吗?
后面加载的覆盖之前的。
文章很有用,作者很用心啊,呵呵。
谢谢认可
Pingback: The ONE使用笔记:目录 | Spark & Shine
楼主太好了,整理出这些有用的东西,新手刚接触,盼望能跟楼主多交流学习!!!
谢谢认可。本站第一个评论。
楼上重邮的啊