下面是小编为大家整理的ONE相关知识整理之二——仿真前初始化(2022年),供大家参考。
ONE 相关知识整理 之 二 —— 仿真前的初始化
仿真实验前的初始化是整个仿真过程中十分关键的一步,它将创建仿真中使用到的节点以及其上的所有“附加物”,如运动模型、路由等,本文将简要介绍 ONE仿真过程中的整个初始化过程。
1、 、 宏观 视角—— 方法 的调用顺序 1.1、读入配置 Core 包下的 DTNSim 是整个 ONE 仿真的主类,它定义了 Main 方法作为整个程序的开始点,在仿真开始前,DTNSim 首先从命令行输入的 args 中取得仿真配置文件的名称,并将配置文件载入到 Setting 对象中,以便后续创建节点、运动模型、路由等过程时读取,这里需要注意的是,无论是否在命令行中有给出配置文件的参数,只要默认配置(default_)存在,它就一定会被读取并写入 Setting对象,而命令行中给出的配置文件仅仅是“覆盖”了默认配置,所以,当仿真实验出现了一些与配置文件相异的情况时,首先要想到是否是因为 default_settings 中部分参数的影响,所以建议直接删掉这个文件,以免影响最终实验结果。
1.2、启动 UI 在读取完数据后,ONE 会根据用户在命令行给出的指令,选择命令行 UI 或图形化 UI 界面对象进行创建,并调用其中的 start()方法,该方法在命令行 UI 和图形化UI的基类DTNSimUI 中实现,分别调用了两个方法,initModel()和runSim(),其中 initModel()由该基类进行实现,完成仿真的初始化过程,runSim()在基类中public static void init(String propFile) throws SettingsError {
String outFile;
try {
//先读入default setting
if (new File(DEF_SETTINGS_FILE).exists()) {
//内部机制是直接通过Properties来读的
Properties defProperties = new Properties();
(new FileInputStream(DEF_SETTINGS_FILE));
props = new Properties(defProperties);
}
被声明为 abstract,根据 UI 的不同,采用不同的实现。
1.3、模型初始化 initModel 行的初始化过程主要分成三步,首先是创建了新的 Settings 对象用以读取刚才由 DTNSim 读入的配置项,随后,调用 SimScenario 的 getInstance 方法获取到当前仿真场景对象,getInstance 方法是 Singleton 思想的体现,在进行调用时,它会检查当前是否已有仿真场景存在,若有,返回该场景,若无,则创建一个新场景。这一过程保证了单次仿真过程中不会有多个 SimScenario 出现。
创建场景的过程即是模型初始化中最重要的一步,它包含了仿真节点的创建(调用 enario 中的 createHosts 方法)与仿真世界(World)的创建(调用对象的参数构造器),前者即是网络中进行通信的节点,是仿真的主体,而后者则负责承载所有节点,当仿真时间线向前推进时,负责调用 update 方法来更新各个节点的状态(移动模型),以及消息(路由)。
当场景创建完毕后,UI 对象会根据 Settings 中定义的 Report 的名称,调用createObject 方法来创建相应的 Report,这一过程是通过 java 的反射机制完成的,该过程是 java 反射机制非常经典的用例(ngs 类 loadObject 方法),通过类名动态找到相应的类文件,调用相应的构造器来创建对象,非常值得研究,因为在平时的学习、开发过程中,很少会直接接触反射(几乎都由 Spring 这样的框架来完成了反射的工作),故,建议详细阅读这部分代码(较长,此处不贴),深入了解Java 中反射的相关机制。
public static SimScenario getInstance() {
if (myinstance == null) {
myinstance = new SimScenario();
}
return myinstance;
}
最后,若在配置文件中定义了移动模型的“预热时间”S,那么 UI 对象将会将仿真时钟 SimClock 回拨 S,并进行移动模型的预热,该过程即让节点根据其移动模型,先进行 S 时间的运动,再开始消息传递。
至此,场景的初始化全部完成。
2、 微观的小坑——节点的创建的细节 SimScenario 中节点创建的过程因为有特殊性,所以值得详细解读。
在 createHost 方法中,节点按照 group 来进行创建,也就是说,同一个 group中的节点的对象是“几乎一样”的,它们拥有完全相同的移动模型、路由模型、网卡和 Application。
对于每一个 group,为了减小创建对象时的开销,ONE 并不是对于每个节点都去创建相应的移动模型、路由模型、网卡和 Application,而是先读取配置项,创建出它们的原型(Prototype)(该过程亦是通过反射完成的),然后在创建各个节点时,各个节点通过调用移动、路由、网卡、Application 类的 replicate 方法,“复制”出一个对象出来,这样减小了直接去构造整个对象的开销,但同时也带来了坑——这一过程调用的是 拷贝 构造器(即形如 Type(Type t)的方法),而不是参数构造器来创造新对象,如下 MapBasedMovement 的 replicate 方法所示
double warmupTime = 0;
//如果移动模型有“预热时间”,那么把模拟器的时钟回拨,以实现预热效果
if (ins(MM_WARMUP_S)) {
warmupTime = uble(MM_WARMUP_S);
if (warmupTime > 0) {
SimClock c = stance();
c.setTime(-warmupTime);
}
} //world对象在场景被创建时,即在构造器中创建了
= .getWorld(); //移动模型的预热 pMovementModel(warmupTime);
当实现自己的移动模型和路由模型时,要特别注意这一点,如果要让这些模型在不同的节点上有不同表现,必须在 拷贝 构造器中有所体现,而不能只在参数构造器中做操作,因为参数构造器仅在创建那个“Prototype”的时候被调用一次!
public MapBasedMovement replicate() {
return new MapBasedMovement(this);
}