<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                這個階段在研究樹莓派,主要用 Node.js 開發。開源社區有許多非常優秀的[樹莓派 Node.js 庫](https://gist.github.com/jperkin/e1f0ce996c83ccf2bca9),但是沒有讓我覺得特別好用的。因為這些庫有的 API 還不錯,但是性能不好,有的性能很好,但是 API 不好用。還有一些庫沒跟上樹莓派的硬件升級,使用起來還要自己修改,作者也不處理 pull request,比較麻煩。因此我自己著手重新寫一個庫?[rpio2](https://github.com/akira-cn/rpio2)。 在寫庫的過程中,自然要充分測試每個 API,個人比較喜歡 TDD / BDD 開發,因此一邊開發功能,一邊寫各個 API 的單元測試。寫了單元測試,自然希望在代碼提交的過程中能夠持續集成,最好是直接使用?[travis-ci](https://www.h5jun.com/post/travis-ci.org)。 愿望是美好的,但是現實有點殘酷。因為樹莓派開發不同于其他軟件開發,它涉及到硬件,雖然說單元測試可以很簡單,主要是測試引腳的同步/異步輸入輸出和事件(中斷)響應,所以開發的時候可以在樹莓派環境里跑單元測試,這沒有問題,但在集成的時候,我們沒有辦法讓 travis-ci 用樹莓派系統環境來跑我們的 test case 吧。這樣的話,就需要我們自己實現對底層的 GPIO 的模擬。 ## 實現對 GPIO 的模擬 聽起來不錯,那就開始干吧! rpio2 庫是基于?[node-rpio](https://github.com/jperkin/node-rpio)?的,選擇這個庫為基礎的原因是,node-rpio 又是對?[bcm2835](http://www.airspayce.com/mikem/bcm2835/)?C 語言驅動庫的 Node 封裝,因為 bcm2835 是用 C 寫的針對 Broadcom BCM 2835 處理器(也就是樹莓派現在使用的 CPU)的底層驅動,因此它可以達到非常高的性能。 使用了 bcm2835 一個額外的好處是,C 語言驅動庫是模塊化的函數單元,這對于測試和模擬來說是友好的,因為只需要知道對應的輸入輸出就可以了。而很多輸入輸出查規格說明書就可以了。 比如:`rpio.open(pin, mode, state)`,當 mode 為 INPUT 的時候,如果不傳 state 參數,默認的輸入電阻是:當 pin <= 8 時,為 PULL_UP,當 8 < pin <= 27 時,為 PULL_DOWN。 這里面比較不好實現的一塊是輸入信號,因為樹莓派可以讓 GPIO 接輸入設備,而輸入設備的輸入信號是實時輸入的,如果在 JS 里用定時器模擬,并不能做到實時同步輸入(因為 JS 是單線程非阻塞模型),所以這里需要考慮多線程,最簡單的方法就是用 File API + child_process。 rpio_sim_helper.js ~~~ "use strict"; const fs = require("fs"); var pin = 0|process.argv[2]; var timers = process.argv[3].split(",").map(o => 0|o); var fileName = "./test/gpio/gpio" + pin; function writeAndWait(value, time){ fs.writeFileSync(fileName, new Buffer([value])); return new Promise(function(resolve){ setTimeout(resolve, time); }); } var p = Promise.resolve(); for(var i = 0; i < timers.length; i += 2){ p = p.then(((i) => () => writeAndWait(timers[i], timers[i + 1]))(i)); } p.then(function(){ var content = fs.readFileSync(fileName); console.log(content); }).catch(err => console.log(err)); ~~~ 上面實現一個簡單的程序來開進程寫文件,例如,要給 rpio21 引腳模擬發送一段半周期為 100 毫秒的脈沖信號,可以這么用: ~~~ node rpio_sim_helper.js 21 1,100,0,100,1,100,0,100...,1,100,0 ~~~ 這樣,將它封裝進模擬庫中: ~~~ helper: function (pin, signal){ if(config.mapping === "physical"){ pin = pinMap[pin]; } return new Promise(function(resolve, reject){ child_process.exec("node ./test/rpio_sim_helper " + pin + " " + signal, function(err, res){ if(err) reject(err); else resolve(res); }); }); ~~~ 就可以很方便地模擬輸入信號了。 ## 對單元測試啟用模擬 這很容易實現,一開始,我打算采用?[proxyquire](https://github.com/thlorenz/proxyquire)?庫,這個庫可以“代理”一個被測試文件里正常 require 的庫,這樣我只要將 rpio2 的 require('rpio') 用自己模擬庫替代就行了。然而實際使用的時候發現并不行。 主要原因是,使用 proxyquire 還是會先加載 rpio 庫,然后才對加載的 rpio 庫進行替換,而 rpio 庫里面有這樣一段代碼: ~~~ var gpiomap; function setup_board() { var cpuinfo, boardrev, match; cpuinfo = fs.readFileSync("/proc/cpuinfo", "ascii", function(err) { if (err) throw err; }); cpuinfo.toString().split(/\n/).forEach(function (line) { match = line.match(/^Revision.*(.{4})/); if (match) { boardrev = parseInt(match[1], 16); return; } }); switch (boardrev) { case 0x2: case 0x3: gpiomap = "v1rev1"; break; case 0x4: case 0x5: case 0x6: case 0x7: case 0x8: case 0x9: case 0xd: case 0xe: case 0xf: gpiomap = "v1rev2"; break; case 0x10: case 0x12: case 0x13: case 0x15: case 0x92: case 0x1041: case 0x2082: gpiomap = "v2plus"; break; default: throw "Unable to determine board revision"; break; } } setup_board(); ~~~ 上面這段代碼通過?`/proc/cpuinfo`?讀取樹莓派的版本信息,然而我本地 Mac 環境里并沒有?`/proc/cpuinfo`,因此加載的時候直接報錯了。而且這個文件單元測試時沒必要加載,直接在測試時加載模擬庫就行了。 因此,更簡單的方法是測試時,將 rpio2 中的?`require('rpio')`?直接替換成加載?`rpio_sim.js`。這一步很多部署工具都可以做,比如 gulp 就是很好的選擇。不過我[用 babel 插件來實現代碼測試覆蓋度檢查](https://www.h5jun.com/post/code-coverage-with-babel-plugin.html),所以我就直接用 babel 來做了,代碼更少,也很方便: transform_rpio_sim.js ~~~ module.exports = function(babel) { var t = babel.types; return {visitor: { CallExpression: function(path){ if(path.node.callee.name === "require"){ var module = path.node.arguments[0]; if(module && module.value === "rpio"){ module.value = "../rpio_sim.js"; } } } }}; }; ~~~ 然后我們將這些結合到一起,在 package.json 中編寫測試命令: ~~~ "scripts": { "test": "babel lib --out-dir test/lib --plugins ../test/transform_rpio_sim.js && mocha test/spec.js", "printcov": "script/printcov.js lib/coverage.lcov lib", "test-cov": "babel lib --out-dir test/lib --plugins ../test/transform_rpio_sim.js,transform-coverage && mocha test/spec.js --reporter=mocha-lcov-reporter > lib/coverage.lcov && npm run printcov" }, ~~~ 這樣就能得到單元測試結果和測試覆蓋度結果: ![unit test](https://p.ssl.qhimg.com/t01a799c7e18bab8f2d.png) ![code coverage](https://p.ssl.qhimg.com/t0116d11f9cda75f6b9.png) ## 開始集成 接下來我們要在代碼提交到 github 時啟用 travis-ci 集成,我們和以前一樣寫一個 .travis.yml 文件: ~~~ language: node_js node_js: - "4" sudo: false script: - "npm run test-cov" after_script: "npm install coveralls && cd lib && cat coverage.lcov | ../node_modules/coveralls/bin/coveralls.js && cd .." ~~~ 但是這里有個問題,因為我們的 package.json 文件里有 rpio 的依賴: ~~~ "dependencies": { "rpio": "^0.9.11" }, "devDependencies": { "consoler": "^0.2.0", "chokidar": "^1.6.0", "wait-promise": "0.4.1", "babel-cli": "6.x.x", "babel-runtime": "6.x.x", "mocha": "^2.3.4", "mocha-lcov-reporter": "^1.2.0", "chai": "^3.4.1", "babel-plugin-transform-coverage": "^0.1.5" }, ~~~ 而這個?`rpio`?在 travis-ci 的集成環境里并不能編譯過去,而且實際上我們測試不需要它。因此,這里需要再寫一段 pre-install 腳本將它去掉: travis_pre_install.sh ~~~ #!/bin/sh sed -i "/"rpio": .*/d" ./package.json ~~~ 然后將這個腳本 hook 到 .travis.yml 的命令中去: ~~~ language: node_js node_js: - "4" sudo: false before_install: - sh travis_pre_install.sh script: - "npm run test-cov" after_script: "npm install coveralls && cd lib && cat coverage.lcov | ../node_modules/coveralls/bin/coveralls.js && cd .." ~~~ 最后,將代碼提交到 github,就能完成自動化的持續集成了:[![build status](https://api.travis-ci.org/akira-cn/rpio2.svg?branch=master)](https://travis-ci.org/akira-cn/rpio2) 自動的單元測試代碼覆蓋度檢查也是正常的(如果現在測試覆蓋度低,請忽略,因為這個庫還在更新中~):[![coverage status](https://img.shields.io/coveralls/akira-cn/rpio2.svg)](https://coveralls.io/github/akira-cn/rpio2) ## 總結 我們要做的事情目標很明確:構建一套適合于樹莓派(實際上也適合于其他硬件開發)的純軟件 TDD 方法。在這里我用到了各種技術的組合來實現我的目的,包括: * 用啟進程讀寫文件的方式來模擬信號輸入 * 用 Babel 插件實現模擬庫代替真實庫 * 用 bash 腳本來實現集成環境的編譯預處理 我們會發現,工程師自己實現一個代碼庫,尤其是涉及工程化的一些工作的時候,知識的廣度會很重要。 而且,合理使用各種知識最終解決問題也是一個有趣的過程,不是嗎? 有任何問題,歡迎留言區討論。 本文鏈接:[https://www.h5jun.com/post/pi-and-tdd.html](https://www.h5jun.com/post/pi-and-tdd.html) --?EOF?-- 作者?[`admin`?](https://www.h5jun.com/author/admin)發表于?*2016-07-28 15:19:53*?,并被添加「?[`JavaScript`?](https://www.h5jun.com/tags/JavaScript)[`TDD`?](https://www.h5jun.com/tags/TDD)[`Pi`?](https://www.h5jun.com/tags/Pi)」標簽 ,最后修改于?*2016-07-28 17:48:25*
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看