做树莓派,为什么还要用nodejs?
没啥特别原因,主要是自己对nodejs+webscoket这套玩的比较熟,而对python的web开发没啥研究
所以,定下这样一个技术方案:python负责硬件驱动和底层逻辑,nodejs负责上面的“好玩”的东西,也就是一般说的“业务逻辑”,web/webscoket服务服务由nodejs提供~
那么问题来了,如何让python和nodejs可以方便的互相调用哪?
按照自己的知识储备,大概有三个方案,按照优先级:
1. 双向的webscoket
2. 单向的thrift,python和nodejs各建一个server端和一个client端,实现双向通信
3. 单向的http,和上面的思路类似,但是速度会比thrift慢一个数量级
webscoket最好用,可以和页面通讯实现极速统一,而且一个连接就可以实现双向通讯,~~但是协议相对复杂,本来想使用nodejs的socket.io,让nodejs做server,python做client,但是试了几种方案都连不上,如果手动实现webscoket通讯协议。。。算了吧,还是下一个吧
第2个方案其实也不顺利,由于对python不熟悉,thrift的库文件导了几次总是失败,拖了几天仔细研究了python的import方式才算成功
这样,整个系统的方案也就是这样
(我的初步打算是做一个能自主行走、壁障,又能用手机网页遥控的机器人,所以硬件部分是直流电机控制芯片、超声测距、红外感应)
下面就来分享一下nodejs与python基于thrift做互联的代码,根据这个方案,对于不熟悉python又想在树莓派上实现较复杂业务逻辑的人,完全可以将nodejs换成自己更熟悉的语言,对于thrift不熟悉的朋友,可以自己学习:http://thrift.apache.org
和一般的thrift的思路不同,为了简化代码逻辑,这里其实类似http接口,action类似url,post一坨键值对,然会返回一坨信息,之所以这么设计,是因为一直觉得thrift太啰嗦,每次添加新接口还要替换定义文件
thrift定义文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /** * 统一的返回值 */ struct Retu{ 1: i32 code, //返回码 2: string msg, //返回错误时的详细信息 3: map<string, string> data//map/字典 类型的返回值 } /** * 异常信息 */ exception UException{ 1:i32 errorCode, //错误码 1=参数错误,2=没有结果 99=系统异常 2:string errorMessage //错误描述 } service PiMessService{ Retu c(1:string action, 2:map<string, string> params) throws (1:UException ex), } |
python端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | #coding:utf-8 import sys, glob, os import event #这里是自己封装的python事件中心,可以订阅、触发事件,以后有机会单独给大家分享 rootPath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(rootPath + '/lib/gen-py') #thrift库文件位置 from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol from thrift.server import TServer import piMess.PiMessService as PiMessService import piMess.ttypes as ttypes #####server start##### class PiMessHandler: def __init__(self): self.log = {} #会被client调用的方法 def c(self, action, params): #收到一次调用,其实是触发一次特定事件,程序其他部分可以去订阅这个事件,返回给出返回值 rets = event.trigger('tele_' + action, params) msg = '' data = {} if(len(rets) > 0): #以第一个绑定的返回值为准,后续可以根据实际需求在扩展 if(type(rets[0]) == type('')):#只使用字符串和字典值 msg = rets[0] elif(type(rets[0]) == type({})): data = rets[0] return ttypes.Retu(200, msg, data) #启动服务 def serviceStart(): print 'PiMessService start on 9001 ...' handler = PiMessHandler() processor = PiMessService.Processor(handler) transport = TSocket.TServerSocket(port=9001) #python server端的端口 tfactory = TTransport.TBufferedTransportFactory() pfactory = TBinaryProtocol.TBinaryProtocolFactory() server = TServer.TSimpleServer(processor, transport, tfactory, pfactory) server.serve() print 'done~' #####server end####### #####client start##### __transport = TSocket.TSocket('localhost', 9002) #nodejs server端的端口 __transport = TTransport.TBufferedTransport(__transport) __protocol = TBinaryProtocol.TBinaryProtocol(__transport) __client = PiMessService.Client(__protocol) def clientOpen(): __transport.open() #供外部调用的向nodejs端发送消息的方法 def send(action, params): r = __client.c(action, params) print('call nodejs:', action, params, r) return r def clientClose(): __transport.close() #####client end####### if __name__ == "__main__": clientOpen() send('py-client', {'QQQ': 'ZZZ', 'abc':'PPP'}) serviceStart() |
nodejs端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | var thrift = require('thrift'); var Thrift = thrift.Thrift; var piMessService = require('../gen-nodejs/PiMessService'), ttypes = require('../gen-nodejs/piMess_types'); /***server start***/ var server = thrift.createServer(piMessService, { c: function(action, params, result){ //server,可以根据action值,调用对应的外部方法 console.log('piMessService.c:', action, params); retu = new ttypes.Retu({code:200, msg:'ok', data:null}); result(null, retu); } }, {}); server.listen(9002); /***server end*****/ /***client start***/ var connection = thrift.createConnection('localhost', 9001), client = thrift.createClient(piMessService, connection); //对外暴漏一个send方法 exports.send = function(action, params, callback){ console.log('exports.send:', action, params); client.c(action, params, function(err, response){ if(response){ console.log('response:', response); } callback && callback(err, response); }); }; exports.clientEnd = function(){ connection.end(); }; /***client end*****/ |
over
———
转载请注明出处:昆仑的山头