JAVA 实践项目 --- 树莓派信息自动化采集后入库项目 (二)

本贴最后更新于 2017 天前,其中的信息可能已经斗转星移

项目源代码可访问我的 github:https://github.com/Spacider/Gather-and-store
如果觉得好的话请给个 star 哦~

项目初始

开发 IDE: IDEA 2018.03 JDK 1.8
开发环境: macOS 10.13.6 (如 windows 请对项目中部分路径进行改写)
数据库: Oracle 11g


正式开发

  1. 用 IDEA 创建一个 maven 项目,这个百度都可以找到,我在这里就不细讲了。
  2. 解决项目所有的依赖 jar 包,在项目下的 pom.xml 文件中添加如下内容:
 <dependencies>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.39</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.6</version>
        </dependency>
    </dependencies>

第一阶段数据的采集

imagepng

这里是我们向树莓派获取温度和湿度的模块示意图,通过 XML 文件的交互就可以获得我们需要的数据,获得到数据以后我们拆分得到的 XML 并向 log 文件中存入我们解析好的数据!

首先,模块结构:
imagepng

然后开始编写代码:

  1. 我们创建一个包为 Server,然后创建一个类 DataServer,这个类的作用是用来模拟树莓派系统的,他的主要职能是接收 XML 文件,并返回对应的数据,这里是模拟,所以我们每次都返回一个相同的数据用于测试。

我们先定义个 Thread 类,以便于配合 while 语句来让 Server 端一直保持接收数据的状态。通过构造器将 Socket 对象传入 Thread 类中,通过 Socket 对象我们就可以对获取对应的流对接收与发送进行控制!

class ServerThread extends Thread{
	private Socket socket;
    public ServerThread(Socket socket){
        this.socket = socket;
    }
     @Override
    public void run() {
    }
}

接下来我们的认识就是重写 run 方法,我们需要接收客户端发来的 XML 文件,由于发送过程中每一行都有一个换行符,所以嵌套一个 BufferedReader 对象时,我们就可以通过 readLine() 方法来根据行读取文件,末尾结束的标志是 </Message>

is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String str = null;
StringBuilder sb = new StringBuilder();
while ((str = br.readLine()) != null) {
    sb.append(str + "\r\n");
    // 如果读取到最后一行,则退出
    if (str.trim().equals("</Message>")) {
        break;
    }
}

为了关闭资源方便,我们用到了关闭资源辅助类:
创建 util 包,创建 IOUtil 方法。

public class IOUtil {
    public static void close(Closeable... closeableList) {
        try {
            for (Closeable closeable : closeableList) {
                if (closeable != null) {
                    closeable.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

读取到 XML 文件时,我们就需要对相应的 Client 端返回一个新的 XML 文件,为了方便我们编写一个 SAXReaderHelper 类来协助我们的操作,编写一个方法 BackXml 来进行相关的处理。该方法通过解析从客户端发来的 xml 文件,获取 SensorAddress 值来模拟传感器的选择,从而返回不同的模拟数据。

  • sensorAddress 为 16 采集到的数据为 10 位,前 8 位为湿度和温度数据,后两位为序号。
  • sensorAddress 为 256 采集到的数据为 6 位,前四位为光照数据,后两位为序号。
  • sensorAddress 为 1280 采集到的数据为 6 位,前四位为二氧化碳数据,后两位为序号。
 /**
     * 根据不同的 SensorAddress 返回不同的XML
     * @param bris
     */
    public static String BackXml(String sb){
        ByteArrayInputStream bais = null;
        // 把客户端传过来的 xml 转化为字符串存储
        String TotalStr = sb.toString();
        byte[] TotalBytes = TotalStr.getBytes();
        bais = new ByteArrayInputStream(TotalBytes);
        SAXReader reader = new SAXReader();
        Document document = null;
        String BackStr = null;
        // 获取根节点
        try {
            document = reader.read(bais,"utf-8");
            Element Message = document.getRootElement();
            String str = null;
            // 获取结点 SensorAddress 的值
            String SensorAddress =  Message.element("SensorAddress").getText();
            if (SensorAddress.equals("16")){
                str = "5d606f7802";
                BackStr = getBakXml(str);
            }else if (SensorAddress.equals("256")){
                str = "5d6002";
                BackStr = getBakXml(str);
            }else if (SensorAddress.equals("1280")){
                str = "5d6002";
                BackStr = getBakXml(str);
            }
        } catch (DocumentException e) {
            System.out.println("返回XML文件失败");
        } finally {
            IOUtil.close(bais);
        }
        return BackStr;
    }
    /**
     * 拼接返回的 XML (提供模拟的 Server 端使用)
     * @param DataStr
     * @return
     */
    public static String getBakXml(String DataStr){
        String str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"+
                "<Message>\r\n"+
                    "<SrcID>100</SrcID>\r\n"+
                    "<DstID>101</DstID>\r\n"+
                    "<DevID>2</DevID>\r\n"+
                    "<SensorAddress>0</SensorAddress>\r\n"+
                    "<Counter>0</Counter>\r\n"+
                    "<Cmd>3</Cmd>\r\n"+
                    "<Data>"+DataStr+"</Data>\r\n"+
                    "<Status>1</Status>\r\n"+
                "</Message>\r\n";
        return str;
    }

在封装了方法以后,我们就可以一行解决问题:

// 根据不同的 SensorAddress 返回不同的XML
String BackStr = SAXReaderHelper.BackXml(sb.toString());

最后通过输出流再次将封装好的模拟数据发送给 Client

os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write(BackStr.toCharArray());
pw.flush();

最后关闭资源~这样我们就编写好了一个模拟的树莓派 Server 端。通过 while(true) 写死循环,这样就可以一直不停的接收数据!

ServerSocket server = new ServerSocket(8888);
while (true){
   Socket socket = server.accept();
   new ServerThread(socket).start();
}

  1. Client 端编写
    Client 端是发送 XML 文件到 Server 端的,其中还是用流进行传输。

首先我们编写 ClientReceiveHelper 类开进行辅助我们操作。

这是拼接发送的 XML 文件的方法!

    /**
         * 拼装发送的 XML 文件
         * @param SensorAddress
         * @param counter
         * @return
         */
        private final static String XmlToSend(String SensorAddress,String counter){
            String str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"+
                    "<Message>\r\n"+
                    "<SrcID>100</SrcID>\r\n"+
                    "<DstID>101</DstID>\r\n"+
                    "<DevID>2</DevID>\r\n"+
                    "<SensorAddress>"+SensorAddress+"</SensorAddress>\r\n"+
                    "<Counter>"+counter+"</Counter>\r\n"+
                    "<Cmd>3</Cmd>\r\n"+
                    "<Status>1</Status>\r\n"+
                    "</Message>\r\n";
            return str;
        }

客户端通过的是 TCP/IP 向 Server 端发送消息。
然后编写发送消息的方法 ClientGetXml

public final static void ClientGetXml(String SensorAddress,int counter){
}

通过 Socket 来开启一个连接

 socket = new Socket("127.0.0.1", 8888);

加锁:考虑到一种特殊情况,socket 交替执行时可能会出现时间片用完的情况,这种情况下可能会导致上一个 socket 的数据传输给了下个 socket 使用,导致数据的错误!
通过发送 XML 让 Server 返回一个 XML 文件给我们,本阶段我们以获取到了上文 Server 发送的 XML 文件为正确。

synchronized (socket) {
       os = socket.getOutputStream();
       pw = new PrintWriter(os);
       String str = XmlToSend(SensorAddress, counter + "");
       pw.write(str.toCharArray());
       pw.flush();

       is = socket.getInputStream();
       br = new BufferedReader(new InputStreamReader(is));
       String Backstr = null;
       // 拼接读取返回的 XML 文件
       StringBuilder sb = new StringBuilder();
       while ((Backstr = br.readLine()) != null) {
           // 在末尾加入 \r\n 方便观察,也方便读取
           sb.append(Backstr + "\r\n");
           if (Backstr.trim().equals("</Message>")) {
               break;
           }
       }
}

编写到这里, 你可以打印一下 BackStr,看下是否发生了死锁(可能未关闭资源或连接未断开等)和传输到的数据是否正确。

经过如上的编写,你就可以编写 Client 如:
方法中的参数为 SensorAddress 的值(判断温度湿度或光照或二氧化碳),和 counter 操作的传感器个数!ClientGetXml 就是上文我们编写的 Client 的操作方法。

public final class GuangClient {
    public static void guangGetObj(){
        ClientReceiveHelper.ClientGetXml("256",1);
    }
}

imagepng

如果你已经获得了 Server 端发送的数据,那么恭喜你,你这一阶段的编写已经取得了成功,可以进行下一阶段的编写!

本文中为代码详解,可能与源代码不一定相同,可以查看我的 github:https://github.com/Spacider/Gather-and-store 查看源代码与你的代码进行比对!如果觉得我写的好的话请给一个 star 哦!

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3168 引用 • 8207 回帖
  • 实践项目
    6 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...