Cooja 中自定义 Java Mote 使用 Collect-View

Cooja 中自定义 Java Mote 使用 Collect-View

受到网上一文的启发。可知 Collect-View中tools/collect-view/src/contiki/collect/ 下的三个类SensorInfo.java、SensorData.java、CollectServer.java 极为重要。经过分析我们知道节点的数据是保存在一个整型数组(如 int[] Value = http://www.ithao123.cn/new int[30] 必须是30,分析SensorData.java 可知)中, SensorInfo 是一个接口,它规定了一个整型数组中每个索引位置的意义,如 Value[0]=30 (必须是30), Value[4] 是一个节点ID的数值Value[1~2]是该组数据更新的时间戳等。
实现原理:节点将 Value[30] 以字符串形式写到一个文件上(但这个字符串前部还需要加上一个系统时间的字符串,分析SensorData.java可知,格式如下图1)。我们可以操作打开文件,读取这一行字符串,用 CollectServer 下的 handleIncomingData(long systemTime, String line) 进行解析,该方法的作用是根据字符串生成 SensorData 对象,并显示数据。
图1:
这里写图片描述

根据我的实验举例:
首先,我要说清楚的是我定义了 AbstractMote 抽象类,并定义了 Mote 和 Sink 类继承于 AbstractMote,分别代表仿真实验中的普通节点和 Sink 节点。使用 Collect-View 插件收集数据的思想是,实验中所有节点将自身的数据按要求写到一个文件中,再用 Collect-View 对象来进行读取并显示。
我在 AbstractMote 类中添加了以下代码(可见,我将数据写在了 SensorDataFile.txt 文件)

public int[] value = http://www.ithao123.cn/new int[30]; //用于保存节点的信息数据

    /**
     * 更新传感器数据数组
     * @param index
     * @param value
     */
    public void updateSensorData() {
        //SensorData中 this.nodeTime = ((values[TIMESTAMP1] << 16) + values[TIMESTAMP2]) * 1000L;
        this.value[SensorInfo.DATA_LEN] = SensorInfo.VALUES_COUNT; //格式要求
        this.value[SensorInfo.NODE_ID] = this.addr.intValue();
        this.value[SensorInfo.NUM_NEIGHBORS] = this.neighbors_number;
        this.value[SensorInfo.BEACON_INTERVAL] = this.cnt_beacon_max;
        this.value[SensorInfo.BATTERY_INDICATOR] = (int) this.battery.getBatteryLevel();
        this.value[SensorInfo.TIMESTAMP1] = ((int)(System.currentTimeMillis()/1000L)>>16);//vaule中是以秒为单位
        this.value[SensorInfo.TIMESTAMP2] = ((int)(System.currentTimeMillis()/1000L));
    }

    /**
     * 传感器数据数组转换为String (前面多加一个系统时间)
     * @return
     */
    public String sensorDataToString() {
        StringBuilder sb = new StringBuilder();
        sb.append(System.currentTimeMillis()).append(' ');
        for(int i=0; i< value.length; i++) {
            sb.append(Integer.toString(value[i])).append(' ');
        }
        return sb.toString().trim();
    }

    /**
     * 将String写入文件
     * @param line
     */
    public void writeSensorData(String line) {
        File file = new File("SensorDataFile.txt");
        BufferedWriter out = null;
        try {
            out = new BufferedWriter(new FileWriter(file, true));
            out.write(line);
            out.newLine();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 周期性调度
     */
    public void updateSensorDataTask() {
        this.updateSensorData();
        String line = this.sensorDataToString();
        this.writeSensorData(line);
    }

节点周期性调度 updateSensorDataTask() 进行一次数据的更新显示,调度的时间根据自身要求设置,它是节点更新数据到 Collect-View 的时间。具体过程首先 updateSensorData() 对 value[] 数组中的数组进行更新,sensorDataToString() 将数组转化为加上一个系统时间的字符串,最后 writeSensorData() 将其写到文件上。
数据写到了文件上,那么接下来就是读取数据了。接下来是读取过程:(不断的读,开线程),我在这里自定义一个类 CollectInfo 来完成此工作。由于读取到的字符串需要 CollectServer 对象来进行解析,所以定义了一个有参的构造函数。代码中有一个睡眠操作,这个可以根据需要设置睡眠的时间,它决定了 Collect-View 的刷新周期。 CollectInfo 类如下:

public class CollectInfo implements Runnable {
    private CollectServer collectServer;

    public CollectInfo(CollectServer collectServer) {
        this.collectServer = collectServer;
    }

    @Override
    public void run() {
        while (true) {
            File file = new File("SensorDataFile.txt");
            if (file.exists() && file.canRead()) {
                String line;
                BufferedReader in = null;
                try {
                    in = new BufferedReader(new FileReader(file));
                    try {
                        while ((line = in.readLine()) != null) {
                            this.collectServer.handleIncomingData(System.currentTimeMillis(), line);
                        }
                        in.close();
                        file.delete();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.currentThread().sleep(10000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

在我的实验中,我是用 Sink 节点来开启收集数据的图形化界面的,于是我在 Sink 节点的初始化函数中,添加了下列代码

        this.collectServer = new CollectServer();
        this.collectServer.start();//启动收集数据的界面
        this.collectInfo = new CollectInfo(this.collectServer);
        new Thread(this.collectInfo).start();//开始收集数据

但这里有一点要注意!! CollectServer 中只有一个 start(SerialConnecction connection) 的启动函数,形参是 Collect-View 中实现的接口类,因为我这里没有用到,所以我在 CollectServer 中重载了一个无参 start() 启动函数,只添加启动的语句。代码如下:

/**
   * 无参重载函数
   * 用于启动收集数据的图形化界面
   */
  public void start() {
    if (hasStarted) {
        throw new IllegalStateException("already started");
    }

    hasStarted = true;

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            window.setVisible(true);
        }
    });
  }

实验结果:由上述代码中 updateSensorData() 可知我关注的节点信息,其中有两个是节点邻居数和电量信息。进行实验结果如下:
这里写图片描述
这里写图片描述

本人学生,能力有限,花了三天做了这个小实验,匆忙写下这博客做为备忘的同时也与大家分享。有分析不到之处望大家谅解与指正,谢谢!

赞 (0) 评论 分享 ()