JAVA實踐專案---樹莓派資訊自動化採集後入庫專案(二)
專案原始碼可訪問我的github:https://github.com/Spacider/Gather-and-store
如果覺得好的話請給個star哦~
專案初始
開發IDE: IDEA 2018.03 JDK 1.8
開發環境: macOS 10.13.6 (如windows請對專案中部分路徑進行改寫)
資料庫: Oracle 11g
正式開發
- 用IDEA建立一個maven專案,這個百度都可以找到,我在這裡就不細講了。
- 解決專案所有的依賴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檔案中存入我們解析好的資料!
首先,模組結構:
然後開始編寫程式碼:
- 我們建立一個包為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();
}
- 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哦!