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哦!

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