上周去了趟广州出差,没及时更新,周一上班摸鱼写写最近的开发经历
1 技术路线研究
我联系了徕卡官方的技术支持,对方表示徕卡是可以通过串口服务器链接互联网的,在确定路线可用之后我开始慢慢去了解这个过程(这里贴一张串口服务器的工作原理,不知道如何去图片水印,有人知道麻烦私信告诉我一下)
大概的路线想明白了,设备价格也还算可以接受,我总共买了两个设备,一个是将RS232转以太网设备,一个是无线上网设备。无线上网的设备比较好理解,插上就可以上网,无非就是账号注册的时候稍微麻烦一点。
串口设备的说明文档走马观花地过了一遍,大致了解串口服务器总共有五种工作模式:TCP服务器、TCP客户端、UDP服务器、UDP客户端、HTTP客户端。因为之前主要的工作全部集中在uni-app这里,理所当然地将HTTP作为主要的研究方向。但是这里和我的需求不相符,我需要的是传送给徕卡设备指令,然后徕卡设备才会发送指令回执,而这个HTTP客户端显然不满足我的需求,想要实现数据的双向传输得用TCP。
然后我尝试使用webSocket去尝试连接TCPServer工作模式下的串口服务器,但是仍然以失败告终。难道又要去翻Native.js?安卓原生开发里面是存在有TCP链接的,无奈只能先去尝试使用安卓原生的功能。但是很快又遇到一个新的问题:串口服务器连接的无线上网设备的ip是根据运营商来确定的,也就是说服务器的ip不固定。
后来一次很偶然的机会,因为这个项目涉及到坐标矩阵的变换,我在解方程的时候看到之前绘制的项目构思,设备直接链接的并不是手机客户端,而是经过了一个服务器,通过服务器来连接所有的其他传感器设备和客户端。那我为什么不使用串口服务器的TCPClient工作模式连接另外一个服务器呢?大致的思路如下所示:
这个服务器同时监听TCP和HTTP请求,这样手机端就不用考虑这么多问题了,只需要正常使用HTTP请求就可以,具体的TCP相关内容丢给后端工程师,我就不用管了,哈哈哈。
2 内容实现
额,高兴的太早了,这个项目只有我一个人,最后还是我自己在处理这个问题,那开始吧,写一个监听TCP和HTTP的服务器。先用Node.js写一个简单的:
var net = require('net');
var http = require('http');
var crypto = require('crypto');
var socketList = [];
var httpServer = http.createServer((request, response) => {
let post = '';
response.on('error', (err) => {
console.log(err);
})
request.on('data', chunk => {
post += chunk;
})
request.on('end', () => {
//接收到消息
var data = JSON.parse(post);
var socketItem = socketList.find(item => item.name == data.name);
//没有找到指定的TCP链接,返回错误
response.writeHead(200, { 'Content-Type': 'text/json' });
if (!socketItem) {
response.end(JSON.stringify({ result: "TCPNotFound", success: false }));
}
else {
var command = `%R1Q,${data.command}\r\n`;
console.log(`来自http请求发送的命令:${data.command}`);
socketItem.socket.write(command)
socketItem.socket.once('data', (data) => {
var dataStr = data.toString();
console.log(`命令响应为:${dataStr}`);
var result = dataStr.match(/(?<=%R1P,0,0:).+?(?=,|\n|\r)/g);
if (!result) {
response.end(JSON.stringify({ result: 'CommandError', success: false, originData: dataStr }));
}
else if (result[0] != '0') {
response.end(JSON.stringify({ result: 'ParamsError', success: false, originData: dataStr }));
}
else {
response.end(JSON.stringify({ result: 'Success', success: true, originData: dataStr }));
}
})
}
})
}).listen(18000);
var tcpServer01 = net.createServer(function (socket) {
let uuid = crypto.randomUUID();
socket.once('data', (data) => {
if (data.includes('leicaConnect')) {
socketList.push({ uuid, name: data.split('|')[1], socket })
console.log(`新的设备连接:\n名称:${data.split('|')},uuid:${uuid}\n`);
}
})
socket.once('close', () => {
let findIndex = socketList.findIndex(item => item.uuid == uuid);
if (findIndex > -1) {
socketList.splice(findIndex, 1);
console.log(`设备连接断开:\n名称:${data.split('|')},uuid:${uuid}\n`);
}
})
}).listen(18001);
console.log('服务器启动成功')
单位内网对ip做了路由处理,无法直接看到自己的外网ip,这个简单,用个内网穿透到指定的外网ip就可以了(主要是目前在测试,服务器上我们的华为云服务器需要写流程很麻烦,我就这样应急了)串口服务器的TCPClient工作模式原理是每隔指定的时间向指定的ip和端口发送连接请求,直到连接成功。同时还可以设置注册包和心跳包,前者是在连接成功之后发送的身份验证一样的东西,后者是每隔一段时间发送一个数据包,确认连接是否完好。
我在这里写了一个简单的注册包,后面可以考虑使用aes加密来处理身份验证的问题。最后将写好的蓝牙模块这里的链接模式替换为HTTP请求就可以了。