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

项目源代码可访问我的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>

第一阶段数据的采集

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

首先,模块结构:
在这里插入图片描述
然后开始编写代码:

  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);
    }
}

在这里插入图片描述

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章