『TinyOS』学習ノート11?


Lesson 11  TOSSIM 
Compiling TOSSIM
 
 make micaz sim
 
 
Running TOSSIM with Python
 
tosのシミュレータはpythonで実行されます
 
TOSSIM simulationを実行するにはrunNextEvent functionを使用します.it returns 0. This means that there was no next event to run. returns 1, there was an event to run.
 
 
Debugging Statements 
 
  • dbg : print a debugging statement preceded by the node ID.
  • dbg_clear : print a debugging statement which is not preceded by the node ID. This allows you to easily print out complex data types, such as packets, without interspersing node IDs through the output.
  • dbgerror : print an error statement preceded by the node ID
  • dbgerror_clear : print an error statement which is not preceded by the node ID  

  •  
    TOSSIM's debugging output can be configured on a per-channel basis. So, for example, you can tell TOSSIM to send the "Boot"channel to standard output, but another channel, "RadioCountToLedsC", to a file. このchannel名は自分で定義することができ,dbgは名前で所望の情報を選択的に出力することができる.例えば、t.addChannel(「Boot」,sys.stdout)は、bootという名前のchannelのdebug情報、すなわちdbg(「Boot」,「Application booted.」)に実行する.これが出力され、dbg(「my」、「Application booted.」)myというchannelが入っていないので出力しません
     
    By default, a channel has no destination and messages to it are discarded.
     
     
    dbg("Boot", "Application booted.
    ");

     
     
    dbg() takes two or more parameters:
  • The first parameter ("Boot"in the above example) defines the output channel. An output channel is a string.
  • The second and subsequent parameters are the message to output and variable formatting. dbg() is identical to a sprintf statement in C++. 

  • debug and error functionsの唯一の違いはhe string output at the beginning of a messageである.Debug statements print DEBUG (n) , Error statements print ERROR (n) .
     
    If a statement has multiple channels and those channels share outputs, then TOSSIM only prints the message once. For example:
     
    dbg("Boot,RadioCountToLedsC", "Application booted.
    ");

     
    if both the Boot channel and RadioCountToLedsC channel were connected to standard out, TOSSIM will only print one message. 
     
     
    Configuring a Network
     
    radio object
     
  • add(src, dest, gain) : Add a link from src to dest with gain. When src transmits, dest will receive a packet attenuated by the gain value.
  • connected(src, dest) : Return whether there is a link from src to dest.
  • gain(src, dest) : Return the gain value of the link from src to dest.
  • threshold() : Return the CCA threshold.
  • setThreshold(val) : Set the CCA threshold value in dBm. The default is -72dbM.

  •  
    MAC object 
     
    The default MAC object has a large number of functions, for controlling backoff behavior, packet preamble length, radio bandwidth, etc. All time values are specified in terms of radio symbols, and you can configure the number of symbols per second and bits per symbol. By default, the MAC object is configured to act like the standard TinyOS 2.0 CC2420 stack: it has 4 bits per symbol and 64k symbols per second, for 256kbps.
     
     
     
    the radio connectivity data can be stored in a flat file, you can easily create topologies in files and then load the file using a Python script and store them into the radio object.
     
    Create a file topo.txt that looks like this 
    1  2 -54.0
    2  1 -55.0
    1  3 -60.0
    3  1 -60.0
    2  3 -64.0
    3  2 -64.0

     
    this file specifies each link in the graph as a line with three values, the source, the destination, and the gain.
     
    This script will read the file and store the data in the radio object:
    >>> f = open("topo.txt", "r")
    >>> lines = f.readlines()
    >>> for line in lines:
    ...   s = line.split()
    ...   if (len(s) > 0):
    ...     print " ", s[0], " ", s[1], " ", s[2];
    ...     r.add(int(s[0]), int(s[1]), float(s[2]))
    

     
    この章では、TOSSIMがテストに使用する完全なpythonスクリプトについても説明します.
     
    #! /usr/bin/python
    from TOSSIM import *
    import sys
    
    t = Tossim([])
    r = t.radio()
    f = open("topo.txt", "r")
    
    lines = f.readlines()
    for line in lines:
      s = line.split()
      if (len(s) > 0):
        print " ", s[0], " ", s[1], " ", s[2];
        r.add(int(s[0]), int(s[1]), float(s[2]))
    
    t.addChannel("RadioCountToLedsC", sys.stdout)
    t.addChannel("Boot", sys.stdout)
    
    noise = open("meyer-heavy.txt", "r")
    lines = noise.readlines()
    for line in lines:
      str = line.strip()
      if (str != ""):
        val = int(str)
        for i in range(1, 4):
          t.getNode(i).addNoiseTraceReading(val)
    
    for i in range(1, 4):
      print "Creating noise model for ",i;
      t.getNode(i).createNoiseModel()
    
    t.getNode(1).bootAtTime(100001);
    t.getNode(2).bootAtTime(800008);
    t.getNode(3).bootAtTime(1800009);
    
    for i in range(0, 100):
      t.runNextEvent()
     
    また、network topologyと関連する信号対雑音比を生成する方法もあります.詳細は原文を参照
     
     
    Variables 
     
    TOSSIM allows you to inspect variables in a running TinyOS program. Currently, you can only inspect basic types. For example, you can't look at fields of structs, but you can look at state variables.
     
    pythonでtinyosプログラム(nesCプログラム)の変数の状況、例えば変数の値、タイプなどを調べるために使用されます.この機能を利用してプログラムの動作状態などを観察することができます.
     
    from sys import *
    from random import *
    from TOSSIM import *
    from tinyos.tossim.TossimApp import *   #   
    
    n = NescApp()  #   
    t = Tossim(n.variables.variables()) #         Tossim  
    r = t.radio()
    
    f = open("topo.txt", "r")
    lines = f.readlines()
    for line in lines:
      s = line.split()
      if (len(s) > 0):
        if (s[0] == "gain"):
          r.add(int(s[1]), int(s[2]), float(s[3]))
    
    noise = open("meyer-heavy.txt", "r")
    lines = noise.readlines()
    for line in lines:
      str = line.strip()
      if (str != ""):
        val = int(str)
        for i in range(0, 4):
          t.getNode(i).addNoiseTraceReading(val)
    
    for i in range (0, 4):
      t.getNode(i).createNoiseModel()
      t.getNode(i).bootAtTime(i * 2351217 + 23542399)
    
    m = t.getNode(0)
    v = m.getVariable("RadioCountToLedsC.counter")  #        
    while (v.getData() < 10):   #getData()         
      t.runNextEvent()
    
    print "Counter variable at node 0 reached 10."
     
     
    【to be cont... 】
     
     
    Injecting Packets
     
    TOSSIM allows you to dynamically inject packets into a network. Packets can be scheduled to arrive at any time. If a packet is scheduled to arrive in the past, then it arrives immediately. Injected packets circumvent the radio stack: it is possible for a node to receive an injected packet while it is in the midst of receiving a packet from another node over its radio.
     
    Tossimシミュレーションにパッケージを追加して送信できます.
     
     
    RadioCountMsg.py defines a packet format, but this packet is contained in the data payload of another format. If a node is sending a RadioCountMsg over AM, then the RadioCountMsg structure is put into the AM payload, and might look something like this:
    AM Header
    RadioCountMsg
    AM Footer
    If it is sending it over a routing protocol. the packet is put in the routing payload, and might look something like this:
    AM Header
    Routing Header
    RadioCountMsg
    AM Footer
    If you want to send a RadioCountMsg to a node, then you need to decide how to deliver it. In the simple AM case, you place the RadioCountMsg structure in a basic AM packet. In the routing case, you put it in a routing packet, which you then put inside an AM packet. We'll only deal with the simple AM case here.
    To get an AM packet which you can inject into TOSSIM, you call the newPacket function on a Tossim object. The returned object has the standard expected AM fields: destination, length, type, and data, as well as strength.
     
     
    from RadioCountMsg import *  #include packet format
    
    msg = RadioCountMsg()           #creates a RadioCountMsg
    msg.set_counter(7);                #sets its counter to 7
    pkt = t.newPacket();                #creates an AM packet
    pkt.setData(msg.data)             #stores the RadioCountMsg in the AM packet
    pkt.setType(msg.get_amType())     #set AM packet type
    pkt.setDestination(0)                      #set AM packet destination
    
    pkt.deliver(0, t.time() + 3)    #delivers pkt to node 0 at the current simulation time plus 3 ticks (e.g., 3ns).
     
     
     
     
     
    【to be cont... 】
     
     
     
     
    Using gdb
    Since Driver is a C++ program, you can use gdb on it to step through your TinyOS code, inspect variables, set breakpoints, and do everything else you can normally do. Unfortunately, as gdb is designed for C and not nesC, the component model of nesC means that a single command can have multiple providers; referring to a specific command requires specifying the component, interface, and command.
     
    $ gdb Driver
    GNU gdb Red Hat Linux (6.0post-0.20040223.19rh)
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB.  Type "show warranty" for details.
    This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1".
    
    (gdb) break *LedsP$Leds$led0Toggle
    Breakpoint 1 at 0x804f184: file LedsP.nc, line 73.
    
     
     
    オブザーバの変数
    nesC translates component names to C names using $. $ is a legal but almost-never-used character in some versions of C, so nesC prohibits it and uses it internally. The leading * is necessary so dbg can parse the $s. With the above breakpoint set, gdb will break whenever a mote toggles led0.
    Variables have similar names. For example, to inspect the packet of RadioCountToLedsC in the RadioCountToLeds application,
     
     (gdb) print RadioCountToLedsC$packet
     $1 = {{header = {{data = ""}, {data = ""}, {data = ""}, {data = ""}, {
             data = ""}}, data = {{data = ""} }, footer = {{
             data = ""}, {data = ""}}, metadata = {{data = ""}, {data = ""}, {
             data = ""}, {data = ""}, {data = ""}}} }
     
     
    For those who know gdb very well, you'll recognize this as a print of an array, rather than a single variable: there are more than 1000 instances of the message_t struct. This is because TOSSIM simulates many motes; rather than there being a single RadioCountToLedsC$packet, there is one for every node. To print the packet of a specific node, you have to index into the array. This, for example, will print the variable for node 6:
     (gdb) print RadioCountToLedsC$packet[6]
     $2 = {header = {{data = ""}, {data = ""}, {data = ""}, {data = ""}, {
           data = ""}}, data = {{data = ""} }, footer = {{
           data = ""}, {data = ""}}, metadata = {{data = ""}, {data = ""}, {
           data = ""}, {data = ""}, {data = ""}}}
     
    If you want to print out the variable for the node TOSSIM is currently simulating, you can do this:
    (gdb) print RadioCountToLedsC$counter[sim_node()]
    $4 = 0
     
    watchpointの設定
    You can also set watchpoints (although, as to be expected, they are slow:
    (gdb) watch CpmModelC$receiving[23]
    Hardware watchpoint 2: CpmModelC$receiving[23]
     
    This variable happens to be an internal variable in the packet-level network simulation, which keeps track of whether the radio thinks it is receiving a packet. So setting the above watchpoint will cause gdb to break whenever node 23 starts receiving a packet or returns to searching for packet preambles.
     
    ブレークポイントの設定
    Generic components add another wrinkle. Since they use a code-copying approach, each instance of a generic has its own separate functions and variables (this is mostly due to the fact that you can pass types to them). Take, for example, AMQueueImplP , which is used in both the radio AM stack and the serial AM stack. If you use gdb on an application that uses both serial and radio communication and try to break on its Send.send, you'll see an error:
    (gdb) break *AMQueueImplP$Send$send
    No symbol "AMQueueImplP$Send$send" in current context.
     
    nesC gives each generic a unique number. So if you have an application in which there is a single copy of AMQueueImplP, its name will actually be AMQueueImplP$0. For example, in RadioCountToLeds, this will work:
    (gdb) break *AMQueueImplP$0$Send$send
    Breakpoint 5 at 0x8051b29: file AMQueueImplP.nc, line 79.
     
    If you have multiple instances of a generic in a program, there is unfortunately no easy way to figure out each one's name besides looking at the source code or stepping into them. E.g., if you application uses serial and radio communication, knowing which stack has AMQueueImpl$0 and which has AMQueueImplP$1 requires either stepping through their send operation or looking at their app.c files.