發表文章

使用 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 公司的情況。也因為如...

那一年,我們一起用Node.js開發3D使用者介面!

圖片
曾經想要用 Node.js 開發一些原生的應用程式或是 OpenGL 程式,但無論怎麼問,無所不答的 Google 大神都給我千篇一律的答案:『使用 HTML5 和 WebGL』。這答案其實不令人意外,因為截至目前為止,大多數學習使用 Node.js 的人都是 Web 開發者,其所提出的解決方案,理所當然都是與『瀏覽器』有關。想來有趣,Node.js 就是『與瀏覽器剛分手的 JavaScript Engine』,為何還要回頭與瀏覽器糾纏不清呢?於是,筆者開發並於 GitHub 釋出了一個 Node.js module - 『 jsdx-toolkit 』,讓 JavaScript 開發人員,可以輕易使用 Node.js 開發原生的桌面應用程式,更重要的是,不但可以寫炫麗的 3D UI,而且完全不需要靠『傻大個瀏覽器』。 如果想要載入圖片,並把物件 3D 旋轉,只需要這樣做: var toolkit = require('jsdx-toolkit'); if (toolkit.init() != toolkit.INIT_SUCCESS) { console.log("Failed to initialize jsdx-toolkit."); process.exit(); } /* Create a new stage */ var stage = new toolkit.Stage(); stage.title = 'Rotate'; stage.resize(500, 500); stage.setColor(0, 0, 0, 255); stage.on(toolkit.EVENT_DESTROY, function() { toolkit.quit(); }); stage.show(); /* Load a image */ var texture1 = new toolkit.Texture; texture1.loadFile('fred.jpg'); texture1.setPosition(100, 100); texture1.rotate(toolkit.Y_AXIS, 30, 0, 0, 0); stage.add(texture1); toolkit....

我的 Kinect 應用程式開發記錄

圖片
話說微軟(Microsoft) 這兩年為了 XBox 360 所推出的 Kinect 造成一股風潮,一夕之間,把 Wii 打的七零八落,取代了其體感遊戲機王者的地位,相比之下,Sony PS3 同一時期所推出的體感裝置,就顯得乏人問津 。 最近,筆者雖然忙錄,被很多麻煩的公事私事綁住,但每當睡前,仍抽空研究一些新技術。一方面是為了促使腦筋轉得更快,一方面是想加強許多 Idea 和 Business Model 的強度和完整性。於是,撿起丟在一旁生灰塵的 Kinect,嘗試著在上面開發一些應用。 由於『 OpenKinect 』眾高手的努力,所以驅動程式不是太大的問題,很久以前就已經能分別在 Linux、Windows 和 Mac 上驅動 Kinect。不過雖說如此,最困難的還是在於辨識演算法的相關設計,對於開發人員來說,你不過就是能得到攝影機的影像,就如同從 Webcam 上得到一樣,其他的圖學運算以及應用還是要自己動手。 當然,如果 Kinect 只是和一般 Webcam 一樣,那新聞媒體和許多人對他的誇讚,就太言過其實。其最大的不同在於,Kinect 有深度感測,這意味著我們可以得到實際的空間資訊,以工程的方式,大大減少辨識演算法的難度,並提升了辨識系統的精準度。 這次的嘗試,焦點放在辨識演算法之上,便開始思索各種方案。過程中,試了數種別人已經開發好的函式庫,都不甚滿意。倒不是效果不好,而是系統需求太大,開發語言大多採用  Java ,所以需要安裝一卡車的模組程式以及 JVM。有些更是對 Linux 的開發者不夠友善,或開發支援不夠完整。無論哪一種現有方案,都不足以讓筆者可以安心建構在上面然後開發下去,因為產品化和易移植性,對筆者來說是相當重要的考量,不是只要學術驗證而已。所以最後決定,自己手動設計演算法,採用 C/C++。 經過這幾天睡前的努力,可以看到開發過程中的截圖,演算法初步已經能辨識手掌,並抓取指尖位置,精準度已達可用的程度。接著,只要多做些簡單的開發,撰寫 X11 輸入裝置驅動程式,就能實際以多點操控的型式,操作螢幕上的所有應用程式。 此外,這裡有一些心得,Kinect 深度感測的數值範圍是 0 ~ 2047,至於得到的深度數值如何換算成實體距離,這邊有個公式可以換算成公尺(Meter)的型式: Real Dist...

用 NodeJS 打造守護神常註程式

寫系統程式很常會需要碰到實作常駐程式,這意味著你必需要讓程式在背景執行,然後提供服務或是等待事件觸發,如『 親手打造一個背景執行的Daemon程式 』所提到,若用 C 語言,做法大致如此: void main() { pid_t pid; pid = fork(); if (pid>0) { exit(0); } } 而在 node.js 上可以使用『 daemon 』模組,輕易打造常註程式。 $ npm install daemon 使用方法如下: var daemon = require('daemon'); /* Become a daemon */ daemon.start(); /* Loop to do something here in background */ 執行後,程式應該會立即返回並結束,但是若用 ps 等系統工具程式去看目前正在執行的 Process,會發現我們的程式已經運行在背景了。

JuDaemon 完全取代 GNOME 的最後一根稻草

我們不得不承認,GNOME 為了整合桌面環境,曾經開發了很多好用的整合性的元件,像是桌面背景控制、圖型化元件樣式、螢幕設定監控和更多桌面功能的設定程式。對於我們這些不愛 新版 GNOME 的使用者來說,常常是改用其他桌面環境(如:LXDE、E17 或單純用 Awesome window manager),再配上 GNOME 的這些整合性元件。 不久前,雖然 GNOME 進入了 3.0 時代,但這些整合性元件依然存在著,並不隨著版本上升而有所太大改變,這對我們這些『不肯升級的使用者』是很好的消息,沒有太大的影響。不過,在最近幾個月,GNOME 已經將這些元件一一消滅,整併進去其他的專屬程式當中。在 Debian 上,gnome-control-center 更是已經毀損(如果你沒有裝新版的 GNOME 3.0),更進一步,許多重要的系統常駐程式一一失效(如:gnome-settings-daemon 和 gnome-power-manager 等),不再如同過去可以輕易單獨使用。 GNOME 所帶來的毀滅,已經嚴重影響到筆者現有的使用,所以打算再次繼續原本停滯一段時間的 Juice Desktop Environment (詳情可參考『 新鮮果汁吧!果汁桌面環境(Juice Desktop Environment)! 』),過去曾開發過『 JuShelf 』,就屬於這計劃的元件之一。 大概評估了工作,主要就是系統監控程式的實現,也就是取代舊有 gnome-settings-daemon 的功能。對筆者個人而言,目前只有幾種簡單需求: 電源管理:螢幕休眠、電腦蓋上後自動休眠、AC 電源切換通知和低電壓通知。 螢幕設定:插上外接螢幕後自動偵測,拔除時也自動恢復成單一螢幕(現在是一片黑)。 OSD支援:調整螢幕亮度、音量時,可以跳出畫面顯示目前亮度和音亮。 日後當然還需要有圖型化程式,可以微調這些細部設定,不過不是初期主要的工作(相信會選擇不用 GNOME 的人,應該都有能力自己改設定檔)。 筆者成立了一個新的 Project -『 JuDaemon 』,就是為了解決這樣的需求,目前只完成了螢幕自動休眠的部份,尚有其他工作待繼續。如果您有興趣,可以提交 patch 或是加入這個專案一同開發。:-) 後記 考慮到...