發表文章

目前顯示的是 3月, 2012的文章

使用 Node.js 控制網路連線管理員

既然以『國內首屈一指的 JavaScript 專家』為目標,使用 JavaScript 打造作業系統就是我們的終極目標。未來會將先前的 Flat Project(可參考舊文 Flat Project Demo - An OS for Tablets 平板作業系統 和 Flat Project - 從山寨做起,親手打造炫麗的平板系統 )以 JavaScript 重新架構,實作並提供一個更易於開發應用的環境。當然,就像過去所說,無論有多困難,產品化一直是我們的目標。 既然要開發作業系統,就要讓 JavaScript 能直接觸碰到系統層級或硬體裝置的控制,我們必須實作各種 JavaScript API 去達成這個目的,而前些日子所提及的『 node-dbus 』和『 jsdx-toolkit 』就是在做這類的工作。現在,基於 node-dbus,我們進一步和『 connman(Connection Manager) 』連接,實作出『 jsdx-connman 』,提供 JavaScript 網路管理機制的 API。這代表我們可以藉由 jsdx-connman 去控制無線、有線網路,甚至是WiMax、藍芽等各種連線介面。 如果想要嘗試 jsdx-connman,可以直接使用 NPM 安裝: npm install jsdx-connman 這邊有個範例,讓我們可以使用 jsdx-connman API 去得知無線裝置的狀態和掃描當前環境的 Wifi 無線基地台: var ConnMan = require('jsdx-connman'); var connman = new ConnMan(); connman.init(function() { if (connman.Wifi.Powered) console.log('Wifi is powered'); else console.log('Wifi is not powered'); if (connman.Wifi.Connected) console.log('Wifi is connected'); else console.log('Wifi is not connected');

這是兩碼子事!『犛清問題』與『不是我的問題』!

前一陣子有人寫了文章,以硬體工程師的角度,跳出來罵軟體工程師,因而吵得沸沸揚揚,在各社群網站上也被人重覆轉貼了好一段時間,掀起不少戰文。雖然整篇文章看下來情緒化字眼相當多,在某些部份也有些偏頗甚至太超過。但是不可否認,他的確有提到一個長久以來,許多工程師的通病,無論是軟體工程師還是硬體工程師。 身為工程師或與工程師一同工作的人,應該常聽到或自己本身常說這句話:『不是我的問題。』,就是這經典名句,總是搞得老闆慌、主管氣、工程師們更怒。而對這句話,我特別有感觸,因為過去我身為一個科技業救火員,常跳入火坑滅火,發現很多時候,就是這句話讓什麼問題都解決不了,導致起了大火,又沒人要解決,最後還需要我這外人進去犛清問題。 而在這些救火經歷中,我深刻體會到,證明『不是我的問題』是非常困難的。 一方面,有原罪加身,別人會把你當做在推卸責任。另一方面,因為非常難用『列舉法』去證明自己沒錯,又加上當局者迷,所以,才會有這類『列舉不全』的自我辯解:『我的程式在我的機器上測一點都正常,所以沒問題』、『我測量的電壓和波形都很正常,所以不是我硬體的問題』。其癥結在於,你有在其他電腦環境上測過嗎?你有測過所有工作狀況之下的電壓和波形嗎?大家似乎都忘了高中數學就學過,要找出『向量』以指出方向,要有兩個或更多個點嗎?就算你要用列舉法證明問題不在於你,你也要取樣更多更完整的證據。 所以,很多工程師想盡辦法證明自己沒錯,以為自己是在犛清問題,根本不然。因為『犛清問題』與『不是我的問題』是兩碼子事啊! 我認為,如果你要犛清問題,又證明自己沒錯,『證明是別人的錯』才是最好的方法,並且有助於專案前進和實質意義。 這邊有個自己團隊最近遭遇的小故事與大家分享: 我們與硬體廠 X 合作開發一個產品,其中發生了一個問題,播放影片時常會鈍鈍卡卡的,此外,因為該產品有觸控面板,如果影片播放中,用手去碰觸觸控面板,影片會立即變很鈍。 由於是 ARM 平台,播放影片都藉助晶片解碼(Decode),所以起初是找晶片供應商 Y 來查明真相,但是,晶片商『並不認為是自己的問題』,他們認定自己已經出了不少貨,平台夠穩定,所以應該也不可能會有問題。此外,因為觸控面板也會影響影片播放,所以供應商 Y 認定,問題肯定出在 X 廠商因自己動了手腳,加了其他模組或軟體所導致。所以,廠商 X 只好摸摸鼻子,請自己的

能處理 Binary Data 的 Node.js Buffer Class

從一開始 JavaScript 就被設計成易於處理 Unicode 的語言,其所有的動作,都無法直接操作 Binary 類型的資料。也就是說,JavaScript 對處裡一個個位元組(byte)的資料是沒輒的,因此使用範圍大幅受限。為了解決這個問題,Node.js另外設計了一個 Buffer class,讓開發者可以處理 Binary data。 為了方便理解,其實我們可以將 buffer class 看做為 C 語言中的 malloc(),使用它就像是和直接系統要一塊原始記憶體來使用。此外,因為 buffer class 被設計成在 V8 heap 之外配置記憶體,因此不受限於 V8 Engine heap 的 1.6GB 大小限制。 你可以用下列三種方式建立 Buffer 物件: /* 建立 16 Bytes 的記憶體空間 */ var buffer = new Buffer(16); /* 直接代入資料陣列 */ var buffer = new Buffer([ 8, 8, 6, 9, 2, 6, 3, 3, 3, 5, 7, 2, 1, 1, 1 ]); /* 直接代入字串 */ var buffer = new Buffer('String!', 'utf-8'); var buffer = new Buffer('String!', 'ascii'); 你可以寫入資料: var buffer = new Buffer(32); /* 寫入一段字串 */ buffer.write('Write something', 'utf-8'); /* 只寫入兩個字元 */ buffer.write('Write something', 2, 'ascii'); 以操作陣列(Array)的方法,單獨存取每個 Byte 的資料: var buffer = new Buffer(32); /* 將每個 byte 歸零 */ for (var i; i < buffer.length; i++) { buffer[i] = 0; } 當然也有像 memcpy() 的資料複製: var buffer1

讓我們用 Node.js 與 DBus 打交道

一個多工的作業系統,最不可或缺的機制就是 IPC,應用程式互相溝通的管道。但在比較複雜的桌面應用,系統核心本身的 IPC 就顯得太過簡單,不敷使用。而『DBus』是一個被廣泛使用的 IPC 機制,擁有權限控管、標準的 Interface 甚至是可跨網路溝通等特性,讓應用程式只要遵循標準的資料結構,就可以將資訊傳遞到另一支程式手上。DBus 尤其是在桌面環境下被使用最多,從網路管理員(Network Manager)、藍芽(Bluetooth)連線管理、亮度/音量變更等各式更新訊息通知(Notification)機制,無一不用到他。但由於 DBus 有眾多複雜功能,撰寫程式去使用他其實不是這麼容易,所以若是能用 JavaScript 來做這項工作,肯定能加快許多。 對 JavaScript 而言,打通了 DBus,意謂著可以控制網路管理員,去建立所有類型的網路連線(無論是有線、無線、需要撥號或 VPN),亦可以連接 iBus(當前主流的輸入法框架)以支援並控制輸入法,甚至是可以控制絕大多數的系統服務,說是打通任督二脈可是一點也不為過。 上網尋找,其實已經有一些人嘗試在為 Node.js/V8 Engine 寫 DBus 的支援,只是多半都不夠完整或 API 的使用上過於複雜,這讓筆者萌生了要自己開發一個新的 Dbus 模組的念頭。但重新開發實在是太累人,於是在網路上眾多的現成品中,挑選了一個 API 比較易於使用的專案『 node-dbus 』,然後為他加上更完整的功能,目前已經小有成果,除了提交回 upstream 外,也已經發佈到 NPM 上。 如果想要使用,你可以直接以 NPM 安裝(註:NPM上註冊的名稱和github上不太一樣): $ npm install dbus 其使用上相當簡單,建立一個自己的 DBus Service 範例如下: var dbus = require("dbus"); dbus.start(function() { var service_path = 'org.freedesktop.DBus.TestSuitePythonService'; var object_path = '/org/freedesktop/DBus/TestSuitePythonObje

我為什麼大舉投入 JavaScript 的相關開發

和我比較熟的人,最近應該都知道,目前我專注於 JavaScript 和 Google V8 engine 的相關研究上,甚至決定從今年開始,公司營運以及技術團隊的方向,是企圖提供『最專業的 Node.js 和 JavaScript 相關服務』。(所以,如果您有需要,請聯絡我們 :-) )我非常清楚知道,有些人聽到 JavaScript 就歡天喜地,而有些人則不將他當一回事。更有多到數不清的技術人員,熟悉並使用 JavaScript 非常多年,聽到我說要提供『最專業的 JavaScript 相關服務』,會不屑一顧甚至以為我在瘋言瘋語。不過,如果你願意聽,接下來我會說明這是怎麼樣的瘋狂服務。 我不能阻止你對 JavaScript 有預設立場,因為這是無奈的歷史包袱,但請暫時放下過去成見,聽我靡靡道來。 我所遭遇的矛盾 過去,絕大多數人都只將 JavaScript 視為 Web 開發的一環,在大家的印象和認知中,JavaScript 充其量只是一個在瀏覽器中控制著 HTML DOM 的腳本語言。雖然近年來出現了 Node.js,過去微軟也支援使用者使用 JavaScript 撰寫 ASP 和系統腳本程式,但也不改這樣的看法:『JavaScript 是身處於末端的語言,與網頁、網站密不可分的東西』。所以,對許多硬底子的企業和研發人員來說,JavaScript 象徵了 Web/HTML 與不彰的效能,一種幻想的存在;而對很多 Web 開發者和前端工程師來說,JavaScript 卻是一種當今資訊產業的萬靈丹。如此天差地遠的矛盾,一直存在於這個產業界。 曾經當過 Web 開發者一段不少的時間,我承認也理解,使用 Web 技術來實作各類使用者界面(User Interface)和資料庫應用,非常容易和快速,不用考慮到太多的事。這一切要歸功於 JavaScript 和 HTML 的合作無間。所以,不時能聽到一些 Web 開發者,嘲笑作業系統程式開發者和嵌入式裝置的技術人員的辛苦,認為許多功能和特效,使用 Web 技術一下就達成了。 由於我過去也是『慣C』一族,做過很多包羅萬象的硬碰硬開發,小從一般電腦到嵌入式裝置、桌面系統到手機平版,大到各類特殊需求的系統研發,都有所涉獵。所以,對於系統資源和效能,一直有著莫明的堅持。也因為長期的磨練,深

Node.js Callback Function 不成文的習慣

我們常會在一些 Node.js 或是第三方模組的 API 上,看到 callback function 的第一個參數是 err 的情況,雖然官方並沒有明文規定(或許有,只是我沒有看到?),但這樣的習慣已經隨處可見。從官方 API 文件中的範例,就能看到許多例子: fs.rename('/tmp/hello', '/tmp/world', function (err) { if (err) throw err; console.log('renamed complete'); }); fs.stat('/tmp/world', function (err, stats) { if (err) throw err; console.log('stats: ' + JSON.stringify(stats)); }); 這樣的情況,最常出現在非同步(asynchronous)執行的函式中。因為這類的函式呼叫,做法是將工作丟到背景等待完成,所以會立即回傳(return),並繼續執行下面的程式。而該函式的工作,在真正完成或有出錯時,會呼叫 callback function。所以,在 callback function 內,我們需要藉由判斷 err 存在與否,去得知該函式是否完成工作或出錯。 至於 err 是什麼格式,對大多開發者來說,他只是個純字串的存在,也就是你可以直接印出它的內容: console.log(err) 但事實上,err 是 Error 物件,這是 ECMA 有定義的標準物件,對一些有經驗的 JavaScript 開發者來說,應該不陌生,甚至除了 Node.js 之外,在其他的瀏覽器上都有支援。如果想知道他是什麼東西,也可以自己嘗試建立一個 Error 物件來觀察: var err = new Error('Hello Error!'); 比較有趣的是,Error 物件被建立時,會包括 JavaScript Engine Stack 的資訊,這有利於更進一步的 Debug 工作。 console.log(err.stack) Error: Hello Error! at repl:1:6 at REPLSe

探討 Node.js 的非同步機制

Node.js 標榜著事件驅動(Event Drive)的設計,也因為如此,它在網站應用程式的領域上,通常強調有最快速的即時反應。其原理是利用 libev 去實作事件輪詢,不斷的檢查是否有事件需要被處理,一旦發現有事件在待命,就去執行並觸發相應的 Handler。但無可避免的,總會有程式和工作需要佔用大量的 CPU 時間,因此獨佔目前的事件處理程序,造成整個程式被單一事件卡死。這樣的問題,有機會讓原本期望的即時反應機制崩潰。 同樣的問題,也出現在其它 non-blocking 設計的 Framework。Facebook 所開發,也紅過一陣子的 Tornado Web Framework,針對這個問題,就提供了一個 Decorator  -『@tornado.web.asynchronous』,這可以讓 Handler 處於非同步的模式下執行,意味著該段程式可以先丟到背景,而不會讓整個程式為了等待該 Handler 結束,而耗時太久或卡死。 相同的,Node.js 也提供擁有類似的設計,只不過對於語言的使用者來說,概念和用法上不太一樣,因為 Node.js 所提供的 API 目標並不只是單純處理 Web Server 的應用,更像是低階的工作排程器的控制,如作業系統上的 yield() 或 sched_yield()。 我們可以很快的透過一個簡單的例子,去理解怎麼使用他,假設我們開一個檔案讀資料,一般邏輯上的做法(這裡所用的 file 物件是假的,在真實的 Node.js API 中並不存在,只是為了說明方便): file.open(); while(true) {     result = file.read();     if (!result)         break;     /* Do something... */ }; console.log('blah blah blah...'); 若是這個檔案很大,需要花三分鐘才能讀完,那這個 while 迴圈肯定會鎖死,等讀完後才顯示『blah blah blah...』字樣。想想看,若是這樣的程式被放在 Web Framework 的 Handler 中,肯定會因為這一個使用者,讓其他人三分鐘之內,都無法使用這個網站服務。而實際上,這樣的問題最常出現於檔案上傳的工作上。

玩什麼雲端?

雲端這個詞已經吵得鬧哄哄,也有更多人為了這個『形容詞』下了太多的定義,到底什麼是雲端已經變得不重要,也無法三言兩語說的清楚。身為一個資訊產業的創業小老弟,其實並不期待雲端能帶給資訊業如何的革命和新生態,只視為這是一次新契機,讓政府和台灣科技業的老大們,能靜下心傾聽。 我們暫且不需要討論雲端的格局觀,因為這類的畫餅,大從國家政策,小到百姓家常,已經太多了。我們有預算,有政策,有土地,有銀行,有股票,什麼都不缺,只缺少夠多的實際作為而已。其實,回顧過去,可以看到許多事我們現在可以做,但是大多數人,多半用美國矽谷的角度探討當今的資訊產業,我認為,那無法幫助台灣人省視情勢,因為有太多經驗不是在台灣的業界可以仿效或改進的。所以,既然人在台灣,當以台灣出發的角度,省視這些年來的發展過程,因此,筆者將以自己在台灣的經驗,嘗試省視這個產業,雖可能不算準確,卻是一種不同角度的嘗試。 的確,筆者剛滿 26 歲,年紀不足,在商業上,經驗也不算多。但在上一個世紀末,卻曾趕上過十幾年前 .COM 的最後一班列車。至今,看著當年遺留下來的無價紀念品,那些重量級的網域名稱,是花再多錢都無法獲得的一種經驗,令筆者有著無限的感慨和體會。說實在,如果沒這樣做過,永遠不會理解,不受金錢誘惑,堅持自己認為正確的事,然後去惡搞並使用那些當年叫價上億美元的網域名稱,是多麼痛快的事。 很多人會說我是忌妒,眼紅看著在十多年前還是小學生時,曾經發想和完成過的東西,諸如網路相簿、Blog、購物車、集體購物等,還有更多當時翹課躲書店而寫出來的服務,在今天一一出現或成功於網路上。事實上,比起心中的不平衡,我開心自己從中學到更多當時沒有看清楚的事,讓今天的視野可以更開闊。 還記得當時,所有人對網際網路(Internet)都有一種特別的期待,就像今天的雲端(Cloud)魔力一樣。全世界的人也都像現在一樣,砸進了無數的人力物力,在商業界,更是起了許多不得了的名目,打算大賺一筆。然而,絕大部份獲得勝利的人是誰?不是那些有錢有勢的企業和組織,而是真正做事和成本相對低的車庫公司。在某方面來說,這些大企業意外吃了不少悶棍。 從國內的發展來看,雖然在網路發達的今天,網路速度已經不再是大問題,架設網站的成本也不是太高。你可能很難想像當年,要是你有著一條 64K 專線,都可以成為台灣 ISP 公司的情況。也因為如此,當時