1. 程式人生 > >JAVA實踐專案---樹莓派資訊自動化採集後入庫專案(四)

JAVA實踐專案---樹莓派資訊自動化採集後入庫專案(四)

專案原始碼可訪問我的github:https://github.com/Spacider/Gather-and-store
如果覺得好的話請給個star哦~

開發IDE: IDEA 2018.03 JDK 1.8
開發環境: macOS 10.13.6 (如windows請對專案中部分路徑進行改寫)
資料庫: Oracle 11g


第三階段:從日誌檔案中採集併發送資料

在JAVA實踐專案—樹莓派資訊自動化採集後入庫專案(三)中寫到將資料寫入日誌檔案中,接下來我們就要從日誌檔案中資料取出來併發送給Server端。
在這裡插入圖片描述

此層專案結構為
在這裡插入圖片描述


接下來開始看程式碼:
1.採集模組:

  • 定義一個Gather藉口
public interface Gather  {
    /**
     * 採集模組介面實現
     * 對 src 下日誌進行處理,一行日誌封裝成一個 Environment 物件或者兩個 Environment 物件
     * 所有采集到的物件封裝到集合中
     * @return
     */
    Collection<Environment> gather();
}

  • 有了介面之後再定義個包為Impl,專門用來放實現類,在Impl下定義一個類為GatherImpl,它實現了Gather介面。
public final class GatherImpl implements Gather  {
}

在實現類中寫一個採集的主方法:

public Collection<Environment> gather(){
}

寫方法實現之前想到一個問題:如果客戶端傳送到伺服器的過程中出現意外怎麼辦?資料是否會丟失?會不會發送錯行?
在經過考慮以後,想出一個解決方案:

使用RandomAccessFile流,它有一個 seek() 方法可以跳過之前已經讀取過的位元組數,這樣的話我們每傳送一次,就把已經發送的位元組數儲存在一個檔案中,每次都讀取這個檔案,保證現在發的是上一次的末尾。

程式碼實現:

  • 讀取儲存檔案,如果檔案不存在,說明第一次執行,則創造一個新檔案:
Properties properties = new Properties();
File positionFile = new File(“/Users/wjh/Desktop/FirstProject/src/main/resources/FilePostion.properties”);
if (!positionFile.exists()){
    positionFile.createNewFile();
}
properties.load(new FileReader(positionFile));
String FilePostion = properties.getProperty("FilePostion");
  • 跳過已經讀取的位元組數,並通過String類的split方法拆分字串
while ((str = raf.readLine()) != null) {
    // 運用 | 來拆分字串
    String[] stringList = str.split("\\|");
    getenv(stringList);
}

// 將最後的位數寫回檔案
properties.setProperty("FilePostion" , position+"");
pw = new PrintWriter(positionFile);
properties.store(pw,null);

getenv方法的作用是根據所傳入的拆分後的字串來生成新的物件並返回,這裡在實際與樹莓派互動過程中出現了髒資料的情況,通過if語句來篩選出正確資料,實現:

private void getenv(String[] stringList){
    String sensorAddress = stringList[3];
    int FinalDate = 0;

    FinalDate = Integer.parseInt(stringList[6].substring(0, 4), 16);

    if (sensorAddress.equals("16")) {
        if (stringList[6].length() != 10){
	        System.out.println("得到的資料為髒資料, 溫度溼度錯誤資料:" + stringList[6]);
        }else {
            /**
             * 溫度計算公式:value(int) float Temperature = ((float)value*0.00268127)- 46.85;
             * 溼度:value(int) float Humidity = ((float)value*0.00190735)-6;
             */
            // 生成溫度物件,並新增到 list 中
            float Temperature = (float) ((FinalDate * 0.00268127) - 46.85);
            environmentList.add(SetNameAndData(stringList, "溫度", Temperature));

            // 生成溼度物件,並新增到 list 中
            FinalDate = Integer.parseInt(stringList[6].substring(4, 8), 16);
            float Humidity = (float) ((FinalDate * 0.00190735) - 6);
            environmentList.add(SetNameAndData(stringList, "溼度", Humidity));
        }
    }else if (sensorAddress.equals("256")){
        if (stringList[6].length() != 6) {
            System.out.println("得到的資料為髒資料, 光照錯誤資料:" + stringList[6]);
        }else{
            environmentList.add(SetNameAndData(stringList, "光照強度", FinalDate));
        }
    }else if (sensorAddress.equals("1280")){
        if (stringList[6].length() != 6) {
            System.out.println("得到的資料為髒資料, 二氧化碳錯誤資料:" + stringList[6]);
        }else{
            environmentList.add(SetNameAndData(stringList, "二氧化碳", FinalDate));
        }
    }else{
        System.out.println("得到的資料錯誤");
    }
}

SetNameAndData方法為不同的 Enviroment 物件封裝名字和資料:

private Environment SetNameAndData(String[] stringList, String name ,float Data){
    String sensorAddress = stringList[3];
    Environment envir = new Environment();
    try {
        envir.setSrcID(stringList[0]);
        envir.setDstID(stringList[1]);
        envir.setDevID(stringList[2]);
        envir.setSensorAddress(sensorAddress);
        envir.setCount(Integer.parseInt(stringList[4]));
        envir.setCmd(Integer.parseInt(stringList[5]));
        envir.setStatus(Integer.parseInt(stringList[7]));
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Timestamp ts = new Timestamp(format.parse(stringList[8]).getTime());
        envir.setGather_date(ts);
        envir.setName(name);
        envir.setData(Data);
    } catch (ParseException e) {
        logger.error("設定名字和資料失敗");
    }

    return envir;
}

在GatherImpl的gather()方法中寫一個列印語句:

for (Environment environment : environmentList) {
    System.out.println(environment);
}

寫到這裡,你就可以通過:

public static void main(String[] args) {
    GatherImpl gather = new GatherImpl();
    gather.gather();
}

來進行測試,最後打印出environment物件集說明書寫正確。


2.傳送模組

  • 同樣,定義一個介面為EnvClient:
public interface EnvClient extends WossModel {
    /**
     * 傳送採集模組採集的集合物件
     */
    void send(Collection<Environment> col);
}
  • 有了介面以後寫它的實現類EnvClientImpl,它有一個很簡單的功能,就是把上文封裝好的物件發給伺服器:
public class EnvClientImpl implements EnvClient  {
	public void send(Collection<Environment> col) {
	}
}

程式碼:

socket = new Socket(host,port);
os = socket.getOutputStream();
oos = new ObjectOutputStream(os);
// 運用物件流把生成的物件發給 Server 端
oos.writeObject(col);
oos.flush();

寫好這一切以後,定義這一階段的的主入口,把內容串起來:

public final class ClientMain {

    /**
     * 集合 Client 並提供向外的入口
     * 通過採集所獲得的 list 發給 Server
     */
    public static void ClientSendMain(){
        ConfigurationImpl configuration = new ConfigurationImpl();
        Gather gather = configuration.getGather();
        List<Environment> environmentList = (List <Environment>) gather.gather();
        configuration.getClient().send(environmentList);
    }
}