TOSSIM是TinyOS自带的模拟器,但是说它是模拟器似乎又不是很准确,按
照文档自己的说法,TOSSIM更像是一个library,就是所谓的类库。需要编译
以后执行。按照我自己的理解,此前你将你的NesC程序编译,安装进传感器,
你的程序执行时是和传感器上的实物硬件沟通,但是使用TOSSIM编译之后,
TOSSIM会模仿系统各个组件的工作,从而你的程序实际上是在和TOSSIM的
组件沟通。说白了就是,TOSSIM把你的NesC程序封装起来了。
不知道里面的措辞是否准确,或者是理解上面是否还有些问题,但是我觉得差不
多就是这个意思。
TOSSIM目前只支持虚拟micaz传感器,但是如果是用来比较两种不同的算法,
或者是纯粹为了调试程序,适用平台不是特别重要。编译也非常简单,就是命令
由此前的:
make micaz
变成:
make micaz sim
一般情况下是不会出现问题的,Ubuntu默认安装了Python。最常见的不过就
是无法找到Python.h文件:
Python.h: No such file or directory
或者是找不到tinyos.tossim.TossimApp包:
No module named tinyos.tossim.TossimApp
两种情况在TOSSIM的文档当中都有介绍解决办法。前者是前往文件
/opt/tinyos-2.x/support/make/sim.extra,在PFLAGS行下指定Python.h
文件的路径,通常是在/usr/include/python2.x/文件夹内,我系统上面的是
2.6。
CFLAGS += -I/usr/include/python2.6/
后者是需要在PYTHONPATH环境变量内添加TinyOS附带的Python API的
路径,我将此行添加在/opt/tinyos-2.x/tinyos.sh文件内:
export PYTHONPATH=$PYTHONPATH:$TOSROOT/support/sdk/python 一般就没什么问题了。编译的过程当中可能会出现一打warning,不是什么大
问题,感觉主要是python版本引起的,只要成功编译就没问题了。
*** Successfully built micaz TOSSIM library.
我目前主要是用来调试程序,所以主要是一个需要输出调试信息,另一个就是需
要访问变量。NesC带来的四条命令,分别是dbg,dbg_clear,dbgerror还
有dbgerror_clear。dbg是输出一条带传感器节点ID的普通调试信息,
dbgerror则是输出一条带ID的错误信息,两者唯一的区别就是输出信息时候
的开头不一样。dbg输出信息开头为DEBUG,dbgerror输出时则显示ERROR。
而带clear后缀的两条命令和前两者使用一样,唯一的区别就是不带ID。
dbg带两个参数,前者是信息输出的频道(不是真正意义上的频道,而是一个
比喻),后者是信息本身,信息的书写和sprintf函数一样,例如:
dbg("Boot", "Mote booted");
则输出Mote booted到Boot频道。不同的频道可以绑定到不同的输出口,可
以直接显示在屏幕上,也可以将输出信息写入文件、以便事后查阅。
现在有一段NesC程序,描述的是Boot接口提供的booted事件:
event void Boot.booted() {
dbg("Boot", "Application booted.
");
dbg("Radio", "Application booted again.
");
}
按照要求,传感器节点在启动以后,需要输出两条消息,Application booted
输出到Boot 频道,Application booted again输出到Radio频道。
执行TOSSIM需要一个简单的Python脚本,可以直接保存进入一个py文件,
由python直接一口气载入执行,也可以在python互动模式下,逐条输入指令
运行。脚本的开头基本都是一样的:
from TOSSIM import *
from tinyos.tossim.TossimApp import *
t = Tossim([]) 因为我从没学过python,所以只能凭着经验去理解这些脚本指令。编译后会生
成一个TOSSIM.py的文件,前两句应该是和Java一样载入文件内的类,第三
句则是实例化一个Tossim类。方括号内是该类构建器的参数,这里默认不带任
何参数。需要知道Tossim类型对象内的属性和方法,则可以通过dir命令:
>>> dir(t)
['__class__', '__del__', '__delattr__', '__dict__', '__doc__',
'__format__', '__getattr__',
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__',
'__swig_getmethods__', '__swig_setmethods__', '__weakref__',
'addChannel', 'currentNode',
'getNode', 'init', 'mac', 'newPacket', 'radio', 'randomSeed',
'removeChannel',
'runNextEvent', 'setCurrentNode', 'setTime', 'this', 'thisown',
'ticksPerSecond', 'time',
'timeStr']
带__前后缀的为对象属性,不带前后缀的则为Tossim类的方法。然后我们可
以使用该对象的getNode方法创建一个节点。
m = t.getNode(1)
这里我们创建一个ID为1的节点m。在NesC代码中,我们的两条信息,是
分别输出到Boot和Radio两个不同的频道,但是目前这两个频道都尚未派上用
场。我们希望Boot频道内的信息直接通过屏幕输出、显示出来,而Radio频道
的信息则写入log.txt文件内。
f = open("log.txt", "w")
t.addChannel("Boot", sys.stdout)
t.addChannel("Radio", f)
m.turnOn() 我们首先以写入模式(w)打开一个名叫log.txt的文件,然后开始向t,也就
是Tossim对象内添加频道。Boot频道被指向sys.stdout,也就是标准输出,
而Radio频道则被添加到f,也就是我们的文件。最后使用m节点的turnOn
方法,启动节点。随着节点启动,booted事件被执行,两条dgb执行也被逐一
执行。不出意外的话,屏幕上会显示Applicaion booted。然后在当前目录下
会多出一个log.txt文件,打开会发现Application booted again.信息。整个
脚本完成的代码是:
from TOSSIM import *
from tinyos.tossim.TossimApp import *
t = Tossim([])
m = t.getNode(1)
f = open("log.txt", "w")
t.addChannel("Boot", sys.stdout)
t.addChannel("Radio", f)
m.turnOn()
你可以直接启动python,逐行输入执行,也可以保存为,例如sim.py文件,
然后通过指令python sim.py来执行。这里Tossim还有一个有用的方法,就
是runNextEvent。顾名思义,就是执行下一个事件。但是在执行事件前,记
得需要先创建并且启动一个节点。
然后是一个简单的查看变量值的例子,TOSSIM目前尚不支持查看struct内的
值,但是可以查看描述程序执行状态的普通变量的值。程序编译完以后会生成一
个app.xml文件,里面包含了所有程序内的变量、组件信息,所以只要把这些
信息“喂”给TOSSIM,TOSSIM便可以在执行过程当中跟踪节点程序的执行情
况。我们把刚才的程序该一下:
uint8_t counter = 0;
event void Boot.booted() {
dbg("Boot", "Application booted.
");
dbg("Radio", "Application booted again.
"); counter ++;
}
也就是变量counter初始化为0,在节点启动之后,自增为1。然后将sim.py
也做相应的修改:
from TOSSIM import *
from tinyos.tossim.TossimApp import *
n = NescApp()
v = n.variables.variables()
t = Tossim(v)
m = t.getNode(1)
m.turnOn()
v = m.getVariable("RadioCountToLedsC.counter")
print(v.getData())
首先是创建一个NescApp的对象,然后通过该对象的variables属性的
variables方法返回程序内所有的变量。然后实例化Tossim对象的时候,将变
量作为参数地交给Tossim类的构建器,这样Tossim执行时便会跟踪程序内的
变量变化。再往后就是前面已经介绍过了,通过getNode创建一个节点,然后
启动该节点。按照程序内的描述,节点在启动之后,counter变量会自增。然后
通过节点的getVariable方法取回counter变量,而参数里面的
RadioCountToLedsC则为NesC组件的名称。最后,用print命令把代表变
量counter的对象v的值显示出来。不出意外的话,显示为1。