做树莓派,为什么还要用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定义文件:
/**
* 统一的返回值
*/
struct Retu{
1: i32 code, //返回码
2: string msg, //返回错误时的详细信息
3: map data//map/字典 类型的返回值
}
/**
* 异常信息
*/
exception UException{
1:i32 errorCode, //错误码 1=参数错误,2=没有结果 99=系统异常
2:string errorMessage //错误描述
}
service PiMessService{
Retu c(1:string action, 2:map params) throws (1:UException ex),
}
python端:
#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端:
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
———
转载请注明出处:昆仑的山头