筆者所知有兩條途徑
libubox 是 OpenWrt 12.9 之後加入的基本程式庫,前面提到的 uHTTPd、netifd 就是奠基在此之上,如果你想用 C/C++ 開發(也有 Lua binding),想要有自己的事件迴圈,追求效能極大化,那你就要了解他。
rpcd 也是建立在 libubox 上,rpcd 之於 ubus 就好比 xinetd 之於 internet,在筆者另一篇文章「Raspberry Pi 筆記: 解決嵌入式系統列印困境」也有提及 xinetd。簡單來說您可以把你的程式掛在 rpcd 之下(稱為 plugin),當 rpcd 收到遠端呼叫(Remote Procedure Call)時(例如前文提到的 jsonrpc),就可以喚起你的 plugin:
接下來看如何開發 plugin。
1. 編寫 plugin script
基本上只要 7688 內能讀寫 stdin/stdout 的程式語言都行,無論是 shell script, Python, Lua, Node...
以 Lua 為例(myrpc):
以 Lua 為例(myrpc):
#!/usr/bin/lua -- /usr/libexec/rpcd/myprc require "luci.json" local json = luci.json if arg[1] == "list" then io.write(json.encode({echo = {arg1 = "str"}})) elseif arg[1] == "call" and arg[2] == "echo" then local echo_arg = io.read("*all") local echo_decoded_arg = json.decode(echo_arg) if echo_decoded_arg.arg1 == nil or type(echo_decoded_arg.arg1) ~= "string" then io.write(json.encode({ret1 = "", ret2 = "invalid argument"})) else io.write(json.encode({ret1 = echo_decoded_arg.arg1})) end endLine 3 取得 luci JSON 編解碼模組。
Line 6-7 列出此 plugin 支援哪些 function 與參數型態。
如果到這裡還是看不懂沒關係,等等實際執行就一目了然了。
2. 測試 plugin
將 myrpc 拷貝到 /usr/libexec/rpcd,然後輸入以下指令:
檢查是否有成功載入:
從這裡可以看到 arg1 參數型態為 String
呼叫測試:
這裡附上 Python 版,因本人非 Python 專家,難免貽笑大方,還請各位見諒 m(_ _)m
#!/usr/bin/python #/usr/libexec/rpcd/myprc2 import sys import json arg = sys.argv if len(arg) == 2 and arg[1] == "list": sys.stdout.write(json.dumps({'echo': {'arg1':'str'}})) elif len(arg) == 3 and arg[1] == "call" and arg[2] == "echo": echo_arg = json.loads(sys.stdin.read()) if 'arg1' not in echo_arg or echo_arg['arg1'] == None: sys.stdout.write(json.dumps({"ret1" : "", "ret2" : "invalid argument"})) else: sys.stdout.write(json.dumps({"ret1" : echo_arg['arg1']}))
3. jsonrpc 測試
同樣拿 rpc_demo.html 進行修改,首先增加一些標籤:<h3> Echo test <⁄h3> Send <input type="input" id="EchoSend"> Receive <input type="input" id="EchoRecv"> <input type="button" value="Echo" id="BtnEcho"> <br><br>增加 rpc_echo()
function rpc_echo(){ config = { "jsonrpc": "2.0", "id": id++, "method": "call", "params": [ session, "myrpc", "echo", { "arg1": $("#EchoSend").val() } ] }; do_ajax(config, function(reply) { if (!reply.result || reply.result[0] != 0){ alert("failed to echo"); }else{ $('#EchoRecv').val(reply.result[1].ret1); } }); }繫結 rpc_echo() 與 button tag:
$( document ).ready(function() { $("#BtnLogin").click(function() { rpc_login("root", $("#Password").val()); }); $("#BtnLogout").click(function() { rpc_logout(); }); $("#BtnMode").click(function() { rpc_wifi_mode($("#Mode").val()); }); //...... $("#BtnEcho").click(function() { rpc_echo(); }); });修改 rpc_login(),增加存取 myprc 權限:
function rpc_login(user, pass) { //...... do_ajax(login, function(reply) { //...... session = reply.result[1].ubus_rpc_session; var grant = { "jsonrpc": "2.0", "id": id++, "method": "call", "params": [ session, "session", "grant", { "scope": "uci", "objects": [ [ "*", "read" ], [ "*", "write" ] ] } ]}; do_ajax(grant, function(reply) { if (!reply.result || reply.result[0] != 0) { alert("failed to grant object permissions"); return; } }); var grant2 = { "jsonrpc": "2.0", "id": id++, "method": "call", "params": [ session, "session", "grant", { "scope": "ubus", "objects": [ [ "myrpc", "echo" ] ] } ]}; do_ajax(grant2, function(reply) { if (!reply.result || reply.result[0] != 0) { alert("failed to grant2 object permissions"); return; } $("#Session").val(session); $("#fwSession").val(session); rpc_board_load(); rpc_system_load(); rpc_wifi_load(); //... }); }); }測試結果:
範例下載
- myrpc(Lua)
- myrpc(Python)
- rpc_demo3.html
沒有留言:
張貼留言