成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Node.js + React Native 畢設(shè):農(nóng)業(yè)物聯(lián)網(wǎng)監(jiān)測系統(tǒng)的開發(fā)手記

renweihub / 822人閱讀

摘要:所幸,鄙人所在的硬件專業(yè),指導(dǎo)老師并不懂軟件,他只是想要一個農(nóng)業(yè)物聯(lián)網(wǎng)的監(jiān)測系統(tǒng),能提供給我的就是一個數(shù)據(jù)庫,帶著一個物聯(lián)網(wǎng)系統(tǒng)運行一年所保存的傳感器數(shù)據(jù)。該物聯(lián)網(wǎng)監(jiān)測系統(tǒng)整體上可分為三層數(shù)據(jù)庫層,服務(wù)器層和客戶端層。的執(zhí)行是同步的。

畢設(shè)大概是大學(xué)四年里最坑爹之一的事情了,畢竟一旦選題不好,就很容易浪費一年的時間做一個并沒有什么卵用,又不能學(xué)到什么東西的雞肋項目。所幸,鄙人所在的硬件專業(yè),指導(dǎo)老師并不懂軟件,他只是想要一個農(nóng)業(yè)物聯(lián)網(wǎng)的監(jiān)測系統(tǒng),能提供給我的就是一個Oracle 11d數(shù)據(jù)庫,帶著一個物聯(lián)網(wǎng)系統(tǒng)運行一年所保存的傳感器數(shù)據(jù)...That"s all。然后,因為他不懂軟件,所以他顯然以結(jié)果為導(dǎo)向,只要我交出一個移動客戶端和一個服務(wù)端,并不會關(guān)心我在其中用了多少坑爹的新技術(shù)。

那還說什么?上!我以強烈的惡搞精神,決定采用業(yè)界最新最坑爹最有可能爛尾的技術(shù),組成一個 Geek 大雜燴,幻想未來那個接手我工作的師兄的一臉懵逼,我露出了邪惡的笑容,一切只為了滿足自己的上新欲。

全部代碼在 GPL 許可證下開源:

服務(wù)端代碼:https://github.com/CauT/the-wall

客戶端代碼:https://github.com/CauT/Night...

由于數(shù)據(jù)庫是學(xué)校實驗室所有,所以不能放出數(shù)據(jù)以供運行,萬分抱歉~。理論上應(yīng)該有一份文檔,但事實上太懶,不知道什么時候會填坑~。

總體架構(gòu)

OK,上圖說明技術(shù)框架。


?
該物聯(lián)網(wǎng)監(jiān)測系統(tǒng)整體上可分為三層:數(shù)據(jù)庫層,服務(wù)器層和客戶端層。

數(shù)據(jù)庫和代碼層

數(shù)據(jù)庫層除了原有的Oracle 11d數(shù)據(jù)庫以外,還額外增加了一個Redis數(shù)據(jù)庫。之所以增加第二個數(shù)據(jù)庫,原因為:

Node.js 的 Oracle 官方依賴 node-oracledb 沒有ORM,也就是說,所有的對數(shù)據(jù)庫的操作,都是直接執(zhí)行SQL語句,簡單粗暴,我擔心自己孱弱的數(shù)據(jù)庫功底(本行是 Android 開發(fā))會引發(fā)鎖表問題,所以通過限制只讀來避開這個問題。

由于該系統(tǒng)服務(wù)于農(nóng)業(yè)企業(yè)的內(nèi)部管理人員,因此其賬號數(shù)量和總體數(shù)據(jù)量必然有限,因此使用 redis 這種內(nèi)存型數(shù)據(jù)庫,可以不必考慮非關(guān)系型數(shù)據(jù)庫在容量占用上的劣勢。讀取速度反而較傳統(tǒng)的 SQL 數(shù)據(jù)庫有一定的優(yōu)勢。

使用非關(guān)系型數(shù)據(jù)庫比關(guān)系型數(shù)據(jù)庫好玩多了(霧

之所以寫了右邊的Git部分,是因為原本打算利用docker技術(shù)搞一個持續(xù)集成和部署的程序,實現(xiàn)提交代碼=>自動測試=>更新服務(wù)器部署更新=>客戶端自動更新 這樣一整套持續(xù)交付的流程,然而最后并沒有時間寫。

服務(wù)器層

服務(wù)器層,采用 Node.js 的 Express 框架作為客戶端的 API 后臺。因為 Node.js 的單線程異步并發(fā)結(jié)構(gòu)使之可以輕松實現(xiàn)較高的 QPS,所以非常適合 API 后端這一特點。其框架設(shè)計和主要功能如下圖所示:

像網(wǎng)關(guān)層:鑒權(quán)模塊這么裝逼的說法,本質(zhì)也就是app.use(jwt({secret: config.jwt_secret}).unless({path: ["/signin"]}));一行而已。因為是直接從畢業(yè)論文里拿下來的圖,畢業(yè)論文都這尿性你們懂的,所以一些故弄玄虛敬請諒解。

客戶端層

?客戶端層絕大部分是 React Native 代碼,但是監(jiān)控數(shù)據(jù)的圖表生成這一塊功能(如下圖),由于 React Native 目前沒有開源的成熟實現(xiàn);試圖通過 Native 代碼來畫圖表,需要實現(xiàn)一個 Native 和 React Native 互相嵌套的架構(gòu),又面臨一些可能的困難;故而最終選擇了內(nèi)嵌一個 html 頁面,前端代碼采用百度的 Echarts 框架來繪制圖表。最終的結(jié)構(gòu)就是大部分 React Native + 少部分 Html5 的客戶端結(jié)構(gòu)。

另外就是采用了 Redux 來統(tǒng)一應(yīng)用的事件分發(fā)和 UI 數(shù)據(jù)管理了??梢哉f,React Native 若能留名青史,Redux 必定是不可或缺的一大原因。這一點我們后文再述。

細節(jié)詳述 服務(wù)端層

服務(wù)端接口表:

?

服務(wù)端程序的編寫過程中,往往涉及到了大量的異步操作,如數(shù)據(jù)庫讀取,網(wǎng)絡(luò)請求,JSON解析等等。而這些異步操作,又往往會因為具體的業(yè)務(wù)場景的要求,而需要保持一定的執(zhí)行順序。此外,還需要保證代碼的可讀性,顯然此時一味嵌套回調(diào)函數(shù),只會使我們陷入代碼幾乎不可讀的回調(diào)地獄(Callback Hell)中。最后,由于JavaScript單線程的執(zhí)行環(huán)境的特性,我們還需要避免指定不必要的執(zhí)行順序,以免降低了程序的運行性能。因此,我在項目中使用Promise模式來處理多異步的邏輯過程。如下代碼所示:

function renderGraph(req, res, filtereds) {
  var x = [];    
  var ys = [];
  var titles = [];

  filtereds[0].forEach(function(row) {
    x.push(getLocalTime(row.RECTIME));
  });

  filtereds.forEach(function(filtered){
    if (filtered[0] == undefined)
      // even if at least one of multi query was succeed
      // fast-fail is essential for secure
      throw new Error("數(shù)據(jù)庫返回結(jié)果為空");
    var y = [];
    filtered.forEach(function(row) {
      y.push(row.ANALOGYVALUE);
    });
    ys.push(y);
    titles.push(filtered[0].DEVICENAME + ": " + filtered[0].DEVICECODE);
  });

  res.render("graph", {
    titles: titles,
    dataX: x,
    dataY: ys,
    height: req.query.height == undefined ? 200 : req.query.height,
    width: req.query.width == undefined ? 300 : req.query.width,
  });
}

function resFilter(resolve, reject, connection, resultSet, numRows, filtered) {
  resultSet.getRows(
    numRows,
    function (err, rows)
    {
      if (err) {
        console.log(err.message);
        reject(err);
      } else if (rows.length == 0) {
        resolve(filtered);
        process.nextTick(function() {
          oracle.releaseConnection(connection);
        });
      } else if (rows.length > 0) {
        filtered.push(rows[0]);
        resFilter(resolve, reject, connection, resultSet, numRows, filtered);
      }
    }
  );
}

function createQuerySingleDeviceDataPromise(req, res, device_id, start_time, end_time) {
  return oracle.getConnection()
  .then(function(connection) {
    return oracle.execute(
      "SELECT
        DEVICE.DEVICEID,
        DEVICECODE,
        DEVICENAME,
        UNIT,
        ANALOGYVALUE,
        DEVICEHISTROY.RECTIME
      FROM
        DEVICE INNER JOIN DEVICEHISTROY
      ON
        DEVICE.DEVICEID = DEVICEHISTROY.DEVICEID
      WHERE
        DEVICE.DEVICEID = :device_id
        AND DEVICEHISTROY.RECTIME
        BETWEEN :start_time AND :end_time
      ORDER
        BY RECTIME",
      [
        device_id,
        start_time,
        end_time
      ],
      {
        outFormat: oracle.OBJECT,
        resultSet: true
      },
      connection
    )
    .then(function(results) {
      var filtered = [];
      var filterGap = Math.floor(
        (end_time - start_time) / (120 * 100)
      );
      return new Promise(function(resolve, reject) {
        resFilter(resolve, reject,
          connection, results.resultSet, filterGap, filtered);
      });
    })
    .catch(function(err) {
      res.status(500).json({
        status: "error",
        message: err.message
      });
      process.nextTick(function() {
        oracle.releaseConnection(connection);
      });
    });
  });
}

function secureCheck(req, res) {
  let qry = req.query;

  if (
    qry.device_ids == undefined
    || qry.start_time == undefined
    || qry.end_time == undefined
  ) {
    throw new Error("device_ids或start_time或end_time參數(shù)為undefined");
  }

  if (req.query.end_time < req.query.start_time) {
    throw new Error("終止時間小于起始時間");
  }
};

router.get("/", function(req, res, next) {
  try {
    var device_ids;
    var queryPromises = [];

    secureCheck(req, res);

    device_ids = req.query.device_ids.toString().split(";");

    for(let i=0; i

這是生成指定N個傳感器在一段時間內(nèi)的折線圖的邏輯。顯然,剖析業(yè)務(wù)可知,我們需要在數(shù)據(jù)庫中查詢N次傳感器,獲得N個值對象數(shù)組,然后才能去用N組數(shù)據(jù)渲染出圖表的HTML頁面。 可以看到,外部核心的Promise控制的流程只集中于下面的幾行之中:Promise.all(queryPromises()).then(renderGraph()).catch()。即,只有獲取完N個傳感器的數(shù)值之后,才會去渲染圖表的HTML頁面,但是這N個傳感器的獲取過程卻又是并發(fā)進行的,由Promise.all()來實現(xiàn)的,合理地利用了有限的機器性能資源。

然而,推入queryPromises數(shù)組中的每個Promise對象,又構(gòu)成了自己的一條Promise邏輯鏈,只有這些子Promise邏輯鏈被處理完了,才可以說整個all()函數(shù)都被執(zhí)行完了。子Promise邏輯鏈大致地可以總結(jié)為以下形式:

function() {    
    return new Promise().then().catch();
}

其中的難點在于:

合理地切分整套業(yè)務(wù)邏輯到不同的then()函數(shù)中,且一個then()中只能有一個異步過程。

函數(shù)體內(nèi)的異步過程所產(chǎn)生的新的Promise邏輯鏈必須被通過return的方式掛載到父函數(shù)的Promise邏輯鏈中,否則即可能形成一個有先有后的控制流程。

catch()函數(shù)必須要做好捕捉和輸出錯誤的處理,否則代碼編寫過程中的錯誤即不可能被發(fā)現(xiàn),異步編程的整個過程也就無從繼續(xù)下去了。

值得一提的是Promise模式的引入。Node.js 自身不帶有Promise,可以引入標準的ECMAScript的Promise實現(xiàn),然而其功能較為簡陋,對于各種API的實現(xiàn)過于匱乏,因此最后選擇了bluebird庫來引入Promise模式的語言支持。

由此我們可以看到,沒有無緣無故的高性能。Node.js 的高并發(fā)的優(yōu)良表現(xiàn),是用異步編程的高復(fù)雜度換來的。當然,你也可以選擇不要編程復(fù)雜度,即不采用 Promise,Asnyc 等等異步編程模式,任由代碼淪入回調(diào)地獄之中,那么這時候的代價就是維護復(fù)雜度了。其中取舍,見仁見智。

客戶端層

客戶端主要功能如下表所示:

?

接下來簡單介紹下幾個主要頁面??梢园l(fā)現(xiàn) iOS 明顯比 Android 要來的漂亮,因為只對 iOS 做了視覺上的細調(diào),直接遷移到 Android 上,就會由于屏幕顯示的色差問題,顯得非常粗糙。所以,對于跨平臺的 React Native App 來說,做兩套色值配置文件,以供兩個平臺使用,還是很有必要的。

?

上圖即是土壤墑情底欄的當前數(shù)據(jù)頁面,分別在Android和iOS上的顯示效果,默認展示所有當前的傳感器的數(shù)值,可以通過選擇傳感器種類或監(jiān)測站編號進行篩選,兩個條件可以分別設(shè)置,選定后再點擊查找,即向服務(wù)器發(fā)起請求,得到數(shù)據(jù)后刷新頁面。由于React Native 的組件化設(shè)計,刷新將只刷新下側(cè)的DashBoard部分,且,若有上次已經(jīng)渲染過的MonitorView,則會復(fù)用他們,不再重復(fù)渲染,從而實現(xiàn)了降低CPU占用的性能優(yōu)化。MonitorView,即每一個傳感器的展示小方塊,自上至下依次展示了傳感器種類,傳感器編號,當前的傳感器數(shù)值以及該傳感器顯示數(shù)值的單位。MonitorView和Dashboard均被抽象為一個一般化,可復(fù)用的組件,使之能夠被利用在氣象信息、病蟲害監(jiān)測之中,提升了開發(fā)效率,降低了代碼的重復(fù)率。

?

上圖是土壤墑情界面的歷史數(shù)據(jù)界面,分別在Android和iOS上的展示效果,默認不會顯示數(shù)據(jù),直到輸入了傳感器種類和監(jiān)測站編號,選擇了年月日時間后,再點擊查找,才會得到結(jié)果并顯示出來。該界面并非如同當前數(shù)據(jù)界面一樣,Android和iOS代碼完全共用。原因在于選擇月日和選擇時間的控件,Android和iOS系統(tǒng)有各自的控件,它們也被封裝為React Native中不同的控件,因此,兩條綠色的選擇時間的按鈕,被封裝為HistoricalDateSelectPad,分別放在componentIOS和componentAndroid文件夾中。界面下側(cè)的數(shù)據(jù)監(jiān)測板,即代碼中的Dashboard,是復(fù)用當前數(shù)據(jù)中的Dashboard。

?

上圖是土壤墑情界面的圖表生成界面,分別在Android和iOS上的展示效果。時間選擇界面,查找按鈕,選擇框,均可復(fù)用前兩個界面的代碼,因此無需多提。值得說的是,生成的折線圖,事實上是通過內(nèi)嵌的WebView來顯示一個網(wǎng)頁的。圖表網(wǎng)頁的生成,則依靠的百度Echarts 第三方庫,然后服務(wù)端提供了一個預(yù)先寫好的前端模板,Express框架填入需要的數(shù)據(jù),最后下發(fā)到移動客戶端上,渲染生成圖表。圖表支持了多曲線的刪減,手指選取查看具體數(shù)據(jù)點,放大縮小等功能。

?

上圖則是實際項目應(yīng)用中的Redux相關(guān)文件的結(jié)構(gòu)。stores中存放全局數(shù)據(jù)store相關(guān)的實現(xiàn)。

actions中則存放根據(jù)模塊切割開的各類action生成函數(shù)集合。在 Redux 中,改變 State 只能通過 action。并且,每一個 action 都必須是 Javascript Plain Object。事實上,創(chuàng)建 action 對象很少用這種每次直接聲明對象的方式,更多地是通過一個創(chuàng)建函數(shù)。這個函數(shù)被稱為Action Creator。

reducers中存放許多reducer的實現(xiàn),其中RootReducer是根文件,它負責把其他reducer拼接為一整個reducer,而reducer就是根據(jù) action 的語義來完成 State 變更的函數(shù)。Reducer 的執(zhí)行是同步的。在給定 initState 以及一系列的 actions,無論在什么時間,重復(fù)執(zhí)行多少次 Reducer,都應(yīng)該得到相同的 newState。

性能測試 服務(wù)端

測試工具:OS X Activity Monitor(http_load)

?

客戶端
iOS

測試工具:Xcode 7.3

?

Android

測試工具:Android Studio 1.2.0

?

代碼量相關(guān)

?

簡單總結(jié)

React Native 盡管在開發(fā)上具有這樣那樣的坑,但是因其天生的跨平臺,和優(yōu)于 Html5的移動性能表現(xiàn),使得他在寫一些不太復(fù)雜的 App 的時候,開發(fā)速度非??欤詭杀?buff。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/80619.html

相關(guān)文章

  • 幾種典型智慧農(nóng)業(yè)聯(lián)網(wǎng)解決方案

    摘要:本文分享幾種典型具有實際應(yīng)用過的智慧農(nóng)業(yè)物聯(lián)網(wǎng)解決方案,供大家參考。智慧農(nóng)業(yè)物聯(lián)網(wǎng)解決方案由三部分組成智慧農(nóng)業(yè)物聯(lián)網(wǎng)平臺智慧農(nóng)業(yè)物聯(lián)網(wǎng)網(wǎng)關(guān)無線節(jié)點。 智慧農(nóng)業(yè)是現(xiàn)代農(nóng)業(yè)發(fā)展的必然趨勢,也是科學(xué)技術(shù)發(fā)展的必然結(jié)果。本文分享幾種典型具有實際應(yīng)用過的智慧農(nóng)業(yè)物聯(lián)網(wǎng)解決方案,供大家參考。 結(jié)合多年與...

    animabear 評論0 收藏0
  • 河北信息服務(wù)業(yè)“十三五”規(guī)劃重點推云計算

    摘要:建立面向中小企業(yè)的信息化服務(wù)云計算平臺,為中小企業(yè)提供生產(chǎn)管理財務(wù)管理營銷管理人力資源管理等云計算服務(wù)?!  逗颖笔⌒畔⒎?wù)業(yè)十三五發(fā)展規(guī)劃》明確,十三五期間,河北省將重點推進云計算服務(wù)能力促進工程、云計算創(chuàng)新能力提升工程、云計算服務(wù)應(yīng)用示范工程、電子政務(wù)集約化建設(shè)工程、數(shù)據(jù)資源開發(fā)共享工程、云計算產(chǎn)業(yè)鏈發(fā)展培育工程、云計算基礎(chǔ)設(shè)施建設(shè)工程、云計算安全保障建設(shè)工程等八大工程,統(tǒng)籌規(guī)劃建設(shè)云上...

    gnehc 評論0 收藏0
  • 最流行編程語言 JavaScript 能做什么?

    摘要:首先很遺憾的一點是,雖然是最好的語言,但是它不是最流行的語言。屬于配置比較高的硬件,而低配的呢三星設(shè)計了引擎,它能夠運行在小于內(nèi)存上,且全部代碼能夠存儲在不足的只讀存儲上。你覺得還能做什么 首先很遺憾的一點是,PHP雖然是最好的語言,但是它不是最流行的語言。showImg(https://segmentfault.com/img/bVvqTs);同時對不起的還有剛剛在4月TIOBE編程...

    褰辯話 評論0 收藏0
  • 最流行編程語言 JavaScript 能做什么?

    摘要:首先很遺憾的一點是,雖然是最好的語言,但是它不是最流行的語言。屬于配置比較高的硬件,而低配的呢三星設(shè)計了引擎,它能夠運行在小于內(nèi)存上,且全部代碼能夠存儲在不足的只讀存儲上。你覺得還能做什么 首先很遺憾的一點是,PHP雖然是最好的語言,但是它不是最流行的語言。showImg(https://segmentfault.com/img/bVvqTs);同時對不起的還有剛剛在4月TIOBE編程...

    Alan 評論0 收藏0
  • 翻譯 | 擺脫瀏覽器限制JavaScript

    摘要:在考慮宇航員的生命安全時,輕微的打嗝或者服務(wù)中斷都會釀成生死事故。也許最大的挑戰(zhàn)來自谷歌主導(dǎo)的簡稱。在最近的開發(fā)者峰會,以及今年的會議上,谷歌都為安排了大量討論。由微軟提供,是廣受歡迎的編輯器,到月份已經(jīng)獲得了超過五百萬用戶。 譯者:安冬 (滬江Web前端開發(fā)工程師)本文原創(chuàng)翻譯,轉(zhuǎn)載請注明作者及出處。原文地址:http://developer.telerik.com/... 技術(shù)世界...

    xfee 評論0 收藏0

發(fā)表評論

0條評論

renweihub

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<