[TIL][Golang] 修復 Golang BLE 在 MacOSX High Sierra 上面的問題

前言

Beacon 是具有藍芽連線的小裝置,通常裡面的格式為 Apple 的 iBeacon 或是 Google 的 Eddystone . 一般而言,要開發 Beacon 的話比較好的方式就是架設一個 Raspberry Pi 然後在裡面灌起來相關的藍芽模擬.但是如果你想要在 MacOSX 上面寫 Beacon 模擬程式的話,大部分的人都會使用 Bleno 一套由 nodejs 開發出來的藍芽套件. 支援夠好又熱門.

Golang 是很方便的語言,不論是寫在後端或是系統上來說.當然我也會想說來寫寫一些測試 beacon 範例的. 三年前,我曾經寫了這篇文章 Golang BLE Eddystone 初體驗(包含Beacon模擬器) 的文章.但是事隔三年之後,由於 Mac OSX 的系統更新,整個部分已經無法正常運行,需要做修改.

在 Golang 上面要使用藍牙的相關工具,大部分人都是使用 paypal 所開源的 PayPal/gatt ,但是那個套件兩年沒有維護了.以下會簡單記錄一下出了哪些問題.

tldr: PayPal/gatt 已經沒有在維護這個軟體.. Golang 要寫藍牙請用 https://github.com/go-ble/ble

Mac OSX 改了什麼

首先 Mac 在 High Sierra 上面將藍牙設備名稱改名字(為何要這樣做?) 原本的名字是 com.apple.blued 但是到了 High Sierra 就改成 com.apple.bluetoothd

這樣導致了 PayPal/gatt 的 golang 套件無法順利運行.其實在 nodejs 的套件上面馬上就跟上了這個部分( nodejs bleno fixed )

Paypal/GATT 已經沒有在維護

回過頭來的老問題,這些問題其實不能解決也不難維護.但是 PayPal/gatt 由於沒有在維護了.其實相當的困難繼續開發,而且許多 fork 出來的專案似乎也沒有比較解決大部分的問題.

換 go-ble/ble

換過去整個流程還算簡單, go-ble 提供 Advertisement 跟 iBeaconAdvertisement.方便提供相關的轉換.

參考

  1. paypal/gatt issue: Examples issue “Go pointer to Go pointer”
    1. GODEBUG=cgocheck=0 ./server
  2. currantlabs/ble issue: Not working on macOS High Sierra
    1. Unhandled event: xpc.Dict{"kCBMsgId":4, "kCBMsgArgs":xpc.Dict{"kCBMsgArgState":5}}
  3. Tracking macOS High Sierra Support

[NodeJS][Golang] 玩玩看 Line Beacon

前言

上週五去參加了 Line 的技術者聚會活動,也同時聽到如何透過 Line Beacon 來跟 Line Bot 結合.於是決定把整個官方範例串接起來,並且將整個流程寫的更容易了解.

LineBot Beacon 流程

在這個流程圖裡面,你可以看到整個流程有一點小複雜:

  • 首先使用者的手機接受到 Beacon 的訊號後,手機會將藍牙訊號傳送到 Line App
  • Line App 收到後,發送 Beacon hardware ID 給 Line Server
  • Line Server 根據 hardware ID 轉發給相關的 Line Bot (也附上該使用者的 token)
  • 這時候 Line Bot Webhook 就會收到使用者發送一個 beacon 的 event
  • 跟據不同 Beacon ID 跟 Event (Enter/Leave) 來定義不同的反應方式

根據這樣的流程,可以知道以下幾件事情:

  • Beacon 的控制的事情其實有限 (Enter/Leave)
  • Beacon event 本身提供訊息也只有以下的部分
string Msg         //beacon string
string type        //beacon type
string hardware ID //beacon hardware ID to determine b

根據這些訊息,其實還是能做出很多有趣的運用就要看各位如何發揮想像力.

沒有 LineBeacon 可以測試? 先試試看官方 nodejs 範例

官方範例: https://github.com/line/line-simple-beacon

node 10 的 MacOSX 藍芽上似乎有問題

先來透過官方範例來看看,結果似乎有點問題 (20180825):

internal/modules/cjs/loader.js:583
    throw err;
    ^

Error: Cannot find module 'xpc-connection'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.<anonymous> (/Users/Evan/src/go/src/github.com/kkdai/line-simple-beacon/tools/line-simplebeacon-nodejs-sample/node_modules/bleno/lib/mac/highsierra.js:10:21)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)

跟工程師討論的結果,似乎 node10 再 xpc-connection (也就是要跑 mac 藍牙的這個部分) 所以可能還是要使用 node8 是比較穩定的.

那來幫 nodejs 降版吧

如果你跟我一樣使用 Homebrew 作為套件的管理系統,那麼從 node10 降板到 node8 的方式如下.

brew uninstall        //uninstall node10
brew install [email protected]   //install node8

這裡要注意,如果以之前有透過 npm 安裝的部分.最好砍掉重新安裝比較穩定.

Line Beacon 綁定在 Line Bot 上

首先先到 [email protected] Manager 來建立開發者帳號 (developer trial) 的 Beacon Hardware ID

記得要將你的號碼掛在你需要附屬的 Line Bot 上面.

LineBot 上面的相關修改

你必須要在你的相對應的 LineBot 上面做了以下的修改,才能讓你的 LineBot 在收到 Line Beacon 訊息的時候給予適當的提醒.

這只是一個最簡單的 code ,但是主要可以顯示出來 Beacon 會傳來兩種訊息.分別是 Enter 跟 Leave

參考鏈結

  1. https://developers.line.me/en/docs/messaging-api/using-beacons/
  2. https://developers.line.me/en/reference/messaging-api/#beacon-event

[Golang][LineBot][2018 更新部分] 透過微軟的語言學習服務 LUIS 架設具有 AI 對話的聊天機器人

前言:

這篇文章其實講解了兩年前為了去 COSCUP 弄的專案(https://github.com/kkdai/LineBotBabyLuis) ,因為當初其實也沒有花太多時間講解開發的內容. 並且也沒有講解如何實現.

如果有興趣當初演講內容的話,可以查看以下兩份投影片:

這篇文章主要講解最近一週,為了讓這個機器人復原所做的一些修改的部分.並且補充了 LineBotLUIS 的執行循序圖,希望讓有興趣的人能夠更清楚的了解.

關於 LineBotLUIS 的簡單介紹:

LineBotLUIS (https://github.com/kkdai/LineBotBabyLuis) 主要是兩年前開發的實驗性質產品.那時候想要結合 NLP 學習的 AI 引擎 (MSFT LUIS) 並且透過 LineBot 的交互式對談的方式,能夠讓這個機器人做到自我學習.

整個機器人就是模擬一個小孩子啞啞學語機器人,大致上流程如下.細節可以再看(循序圖):

  • 輸入小孩子講的話語 (e.g. shi shi, mi~ mi~, ㄋㄞ ㄋ ㄞ ..)
  • 如果她已經學會了,他會告訴你以往的意圖.如果不會它會問你說這句話的意圖是什麼
  • 你可以從 抱抱,牛奶,奶嘴或是玩具之中選擇一個小孩子可能的意圖.(可能就是你了解你小孩子想什麼之後)
  • 然後透過 LUIS 就會學習到,並且之後打出類似的話語就會辨識出來小孩子的意圖.

聽起來很神奇?其實並不難…

架構與近期修改的部分:

這個部分除了 LineBot Webhook (https://github.com/kkdai/LineBotBabyLuis) 的相關處理外,另外還有一個要處理 LUIS API 的 LUIS SDK .這邊是透過我兩年前開發的套件 (https://github.com/kkdai/luis) ,並且也於日前將 API 升級到了 2.0 .

其實本來也沒有太大問題,不過主要是因為微軟日前已經將 LUIS 1.0 API deprecated 掉了.導致我必須將相關的 API 服務都改成 2.0 的服務.

最後, LineBot SDK 也有一些改動.兩年前的 Token 傳遞方式也有變動,造成我必須更新 LineBot Go SDK 的版本之外,其實他們對於 PostAction 的反應也有修改.這邊也會稍微提一下.

關於 LUIS v2.0 修改的部分:

講到 LUIS,就是微軟開發的語意學習引擎 (luis.ai),他將每一句會區分為 Intent(意圖)Entity (物件) .而學習出來的機器模型會根據你輸入的 utterance(話語) 來 Predict(預測) 你的意圖.

先來提提 LUIS 改變到 v2.0 API 的相關修改:

  • Training 跟 Publish 分開來,Training 後必須要 Publish 才能讓你的 Prediction 拿到最新的資料.
  • Prediction 的 API 網址有變動.這邊比較奇怪的是,他不像是一般的 API 從 1.0 換到 2.0 而是整個網址換掉.並且跟其他的 API entrypoint 都不一樣.
  • 其他還有就是要加入 Intent 變得更加的麻煩,不過還好 LineBotLUIS 本身並沒有提供新增 Intent 的方式.

關於 LineBot SDK 的更新:

那回過頭來談談,這一年多來其實 LineBot Go SDK (https://github.com/line/line-bot-sdk-go) 也有一些修改.加上 Line Bot Webhook 的 calling sequence 也有一些些修改如下.

  • Token 的存放位置有變,這邊並不需要改 code ,你只需要更新 Line-Bot-SDK-Go 套件就可以了.
  • 對於 PostAction 的回覆,之前只會傳回一個 Message Event 為 EventTypePostback .更改過後,會傳兩個進來.一個為原有的 EventTypePostback ,又多傳一個 TextMessage. 根據官方部落格文章 開發LINE聊天機器人不可不知的十件事 ,可以了解官方建議你如果要設定 PostAction 的文字,就必須要設定一個無法再 TextMessage 裡面產生作用的特殊字元組合. (e.g. [我要喝牛奶mileQQ~~~]) 類似..

LineBotLUIS 的學習循序圖 (Sequence Diagram):

以上是如何讓 LUIS LineBOT 學習的循序圖,幾個重點可以跟大家分享一下.

  • 使用者輸入任意的文字
  • 如果是 LUIS model 不認識的,或是無法找到最高分的意圖(intent)
  • 這時候就會透過 LineBot Webhook 傳回 Post Action 也就是會顯示目前系統本來就有的意圖(intent)來讓使用者選擇
  • 這時候使用者選擇相對應的意圖後,馬上就會加入該話語到意圖中.並且啟動訓練(training),這就是故意要造成 Streaming Learning 的狀態.
  • 除了要 Training 之外,還要馬上將訓練好的模型發布(publish)出來.讓下一次輸入類似的話語就會找到相對應的意圖.

找得到的循序圖就相對應的簡單,只要找到相對應的意圖馬上就回覆給使用者.

待續

LineBotLUIS 透過呼叫微軟的 LUIS 人工智慧學習引擎,企圖製造出一個會學習的簡單機器人.並且透過 LUIS 似乎也可以打造出一個雖然沒有任何資料庫,但是也不會因為 Heroku 而會重新開始的學習機器人.

[Golang] 在 Kubernetes 上面透過 Go 開發 GRPC service 可能遇到的問題

前言:

之前為了幫公司的產品加上一個中間層的服務處理,需要把許多原先預設的 GRPC 服務參數都改掉,或是新增不少功能.在這裡整理一下,分享給大家.

本文會提供一個簡單的 repo 裡面有提到會使用到的一些功能,並且附上一個 Kubernetes 部署的 yaml 檔.

Show Me The Code

不囉唆,先看 REPO https://github.com/kkdai/grpc-example

原先的 GRPC 服務開始擴展:

一開始,我們先以 Hello World 為範例,並且省略相關的部分. 一開始 SayHello 的範例建置好之後. 這個範例(hello world) 相當簡單,就是一個 SayHello 然後傳回一個回覆. 我們可能會開始收到其他的需求…

以下的需求最後將整合到一個範例 repo https://github.com/kkdai/grpc-example

1: 能不能一次送多個指令 (streaming request)

你可能收到需求,需要在原先的服務上面新增一個接口可以處理連續性的請求 (streaming request) .或是在處理資料的時候不要一次都處理完才回復,而是處理完其中一個部分請求就先回傳 (streaming response).

這是一個相當範例,可以看到 SayStreamHello 並沒有增加新的資料欄位.而是沿用舊的資料欄位

rpc SayHelloStreamServer (HelloRequest) returns (stream HelloReply) {}

在 proto buffer 裡面其實要改得很少,但是處理方式就不太依樣.以下開始放相關的處理代碼.

首先看 Streaming Server 的 server 端的部分

這邊稍微解釋一下,由於你將回傳的資料改成了 streaming 的結果.所以你必須要將資料一筆一筆的傳回去. 這段範例中,我加上了兩秒的 sleep 讓 streaming 更有感覺.

再來看 Streaming Server 的 client 端的部分

由於資料都是由 stream.Recv() 取出來,所以你需要做檢查是否資料傳完了沒.這裡是透過 err == io.EOF 來檢查.

Streaming client 的部分,由於差不多請直接看程式碼 SayHelloStreamClient

2. 如何控制 GRPC service 的連線時間?

接下來能夠連續傳送資料之後,再來就是你會希望能夠讓你的 GRPC call 能夠更久一點.那麼你就會需要去修改你的連線方式.

這段程式碼裡面,可以看到 20*time.Second 就是我們連線的時間限制.你可以延長這個時間來達到增加呼叫時間,當然你也可以換個方式透過這個方式來控制你的時間限制.

3. 能不能大量的資料?

既然你的 GRPC 服務已經可以傳送大量的資料,那麼接下來就會有另外的需求: 能不能傳送相當大量的資料呢? 這裡定義的大量資料可能是超過 5MB 以上,因為 GRPC 預設的大小限制為 4 MB (source code)

	defaultServerMaxReceiveMessageSize = 1024 * 1024 * 4

那如果要修改的話,就依照以下的方式來修改.

這一段是要改成 server 將傳送的大小限制從 4MB 改到 8MB ,因為傳送資料是在 client –> server 這邊做第一次的控管,所以是由 server 來決定最多能傳送多大的資料.當然如果你希望 server 回傳資料可以傳大量的資料,就反過來要改在 client

當然,如果你想改 python 的 grpc server ,可以參考一下方式:

另外一方面.. C++ 的 GRPC server 可以參考:

[更新 20180819] 4. 關於部署到 Kubernetes 可能發生的問題

要部署到 Kubernetes 上,需要兩部分的 yaml 設定.而這裡可能會有一些問題發生. 首先讓我們來看 Deployment 的部署 yaml

Kubernetes Deployment yaml file

這邊的部署方面沒有太多問題,當初在 Dockerfile 設定也是將 clientserver 的檔案包在同一個 docker iamge . 然後透過不同的 entrypoint 來修改.

Kubernetes Service yaml file

這邊設定還算簡單,但是可能有個雷在這裡要小心.

關於 Kubernetes 服務 (service) 部署的小雷

這邊主要要注意到 service 的 selector 設定,要注意好

  selector:
    name: grpc-example 

如果設定不正確,將會導致 client 再透過 grpc-example.default 的連線方式如法正確找到 service .除錯的方式如下

除錯方式:

第一個先透過 kuberctl get endpoints 來確認每個服務(service) 都有相對應的 endpoint ,如果設定不正確可能會出現 endpoint 是空的狀態.

這時候建議回來檢查 service selector 確認是否能夠跟原本的 deployment 對應再一起,這裡建議的 selector 方式使用 name 來找是最簡單的.

總結:

這篇文章整理了幾個容易在撰寫 GRPC 服務的時候卡關的問題,希望能幫助到大家.所有的相關範例程式碼在: https://github.com/kkdai/grpc-example

參考

  • https://stackoverflow.com/questions/42629047/how-to-increase-message-size-in-grpc-using-python
  • https://nanxiao.me/en/message-length-setting-in-grpc/

[好書分享] 創意電力公司:我如何打造皮克斯動畫

前言:

很久沒有看實體的雜書(非程式設計相關的書籍),這一本很早之前就放在我的購物車裡面. 不過因為最近有朋友推薦了另外一本書,就一併買了下來.

但是開始看就欲罷不能,想不到一個週末就把它看完了. 內容讓我相當喜歡,決定想把自己一些心得拿來跟大家分享一下.

內容簡介:

作者 Ed Catmull 是皮克斯動畫工作室的創辦人之一.這本書就是敘述他如何創辦這間公司,並且公司被盧卡斯工作室賣給賈伯斯之後.進而發光發熱製作出許多膾炙人口的動畫影片 “玩具總動員” ,”海底總動員”, “蟲蟲危機” .近幾年更是被迪士尼所收購,成為夢想工廠重要的一員,進而製造出史上動畫最知名的”冰雪奇緣” .

整本書的內容圍繞在如何打造出一個具有創意的團隊.

從第一部的開端,如何創造皮克斯開始.這部分主要是傳達皮克斯建立的環境之外,還有他如何招募進來核心的員工,主要招募的準則就是找進來比他自己還聰明的人. 到了遇到了賈伯斯,離開盧卡斯之後.皮克斯也不是一凡風順的,一開始皮克斯是在賣圖像電腦(也就是影音剪輯電腦,搭配著賈伯斯當時買下的 NeXT 電腦.面臨著只賣出三百台的慘淡業績,讓他們好好思考要從事他們的熱情所在”電腦動畫”,於是乎他們跟剛好需要創新題材的迪士尼合作,簽下來發行三部動畫電影的合約.接下來,就是孕育出令人驚奇的”玩具總動員”.

這邊有個書上提到有趣的小插曲,就是賈伯斯看完”玩具總動員”的內容之後.就加緊找了 Ed Catmull 說 : “ 這部電影一定會讓皮克斯大紅特紅,我們要準備好各種條件.讓迪士尼接下來跟我們簽約的時候給予最好的報價”. 於是在玩具總動員上市的前一個月,他們不是忙著宣傳.而是不斷地尋找上市的相關準備,力圖在玩具總動員開演後一週上市. 當然接下來的故事就為人熟知,玩具總動員上映後打破了影史上相關的紀錄.不論是影評還是票房都是佳評如潮. 皮克斯也如願上市,並且之後跟迪士尼簽訂了更多的合作影片合約.

第一次重大的公司危機就發生在玩具總動員大成功之後.團隊所有人都忙著在做”蟲蟲危機”,而玩具總動員第二集由於被定義成續集,於是公司是透過另外一組人馬來製作. 這時候就發生了第二集的故事劇情令人不滿意,於是乎皮克斯把原本的劇情完全推翻.並且把公司全部人馬又找回來傾全力製作第二集.經過了這次的事件,皮克斯也更深信他們的核心思想 - “有趣而打動人心的故事”.

到第二部的實際運作創意團隊需要的重要條件,舉凡保護新點子,誠實與坦率的智囊團會議是一個很有趣的點子.我們都以為創意是靈光一現,但是他們告訴你創意就像是你的程式碼一樣,需要不斷檢查,運行後進行討論修改.任何我們熟知的熱門電影一開始都是完全不一樣的故事.因為一開始故事的發想是透過編劇與導演來討論出來,這時候會經過皮克斯內部的”智囊團”,是由一群專門”說故事”的人來討論故事的內容.但是皮克斯的智囊團制度有個很有趣的地方是,權力還是在導演的身上.也就是導演可以完全不聽信智囊團的建議. 但是由於智囊團的討論方式相當的正面,於事整個故事的打磨可以在非常順暢的狀況下進行.

恐懼與失敗的章節裡面更形容出創意人有趣的部分.我們都會以為創意人是具有強烈個人意見的,不容易聽信別人的建議. 並且很害怕失敗的發生. 但是在皮克斯裡面,第一個守則是 “一定會發生失敗”. 因為創意不是一次就能到位的東西,是需要不斷修改才能誕生的.這邊舉了一個例子是 “怪獸電力公司”.原本的點子是一個三十歲的會計師與他的故事書的插曲,由於故事書裡面的怪物跑出來後來變成他的朋友. 大家都知道後來的怪獸電力公司是完全不一樣的故事,這也是告訴我們就算一開始的構想是不夠完美的,但是透過團隊不斷的討論後來就能發展出打動人心的好故事.

第三個章節敘述了建立與維持,一個好的創意團隊需要不斷的自主學習與充電.由於皮克斯是充滿創意的公司,於是更需要大家有不斷的”養分”灌溉. 於是有了”皮克斯大學” .公司內部的類似讀書會的組織,並且定期找專業講師來上課.比如說很多電腦動畫師其實並不是美術相關科系出身,於是公司找了專業的美術老師來教導繪畫,讓每個工程師能夠更了解美術人員看東西的角度.

最後一個部分討論到了新挑戰,也就是後來皮克斯被迪士尼併購之後的故事.雖然皮克斯是被併購的公司,但是動畫的相關部門全部都由 Ed Catmull 統一來管理.這時候兩家部門的合併引發了不少直得討論部分. 首先幾個創辦人花了很多時間在兩個部門跑來跑去,並且導入皮克斯的智囊團到迪士尼的內部.讓他們許多傳統動畫的製作部分有了更多的故事琢磨的空間.

最後一個附錄討論了賈伯斯,由於賈伯斯才能造就皮克斯在最艱困的時候能夠堅持做自己想做的電腦動畫.所以最後一部分的附錄就討論了關於賈伯斯的部分,這部分我想有看過傳記可能會知道,就不多提了.

心得:

創意團隊的建立比起我想像中的更難,不僅僅因為創意團隊需要很多的討論.所以人與人之間的交流變得更加的重要,不像是程式碼的世界一樣,一個動人的故事需要注入許多人的經驗與歷練分享,才能討論出令人驚豔的故事.而創意的產生過程會經歷許多的撞牆期,這時候絕對不能害怕失敗.要大膽的失敗才會有創意被激發出來.

許多人也在說軟體工程師也是一個創意的產業,但是我認為軟體工程師比較像是建築工人,不斷地吸收養分進來讓我們能了解如何蓋房子才能又快又好.但是一個好的房子還是會需要不斷的進步,並且測試與改進.

建立一個以專業的團隊相當的困難,創意的團隊更難.需要更大量的人員討論空間,更要有能夠容忍失敗的空間.這些部分都是很值得好好學習的部分.

[投影片分享] Introducing Go channel and pipeline

鏈結

投影片

前言

前幾天在公司的內訓投影片,簡單介紹如何應用 Buffered Channel 跟 Unbuffered Channel 並且導入在 data pipeline 的應用

結果公司內部激發 Go 核心討論.. 讚讚

內容簡介

主要是講解 Go channel 跟 pipeline 的運用. 其中內容會帶到 buffered channel 與 unbuffered 的運用場景. 並且解釋 fan-in 與 fan-out .最後會帶到 pipeline 寫法與介紹 go-kit 裡面 endpoint 的用法.

補充

這個投影片是透過 Present 所製作的,這個算是官方常在 GopherCon 使用的投影片工具.主要有幾個特點:

  • 簡潔
  • 可以顯示程式碼
  • 還可以線上修改與執行

幾個點需要補充一下:

  • 線上修改 Present 的程式碼需要使用 “shift + enter” 來做換行.不然會有問題.

  • 除了顯示程式碼,也可以顯示圖片,但是常常需要調整顯示大小