前言
不知道能写多久的开发日志,先将就着写着看看,也算是对自己当前的总结和未来的鞭策吧。
- 本人从21年10月份开始接触前端开发,22年3月份开始接触app开发,对一些原生的安卓语言可能不太了解甚至可能会有错误,各位看官如果看的尽兴还请多多指导
- 项目的主要开发环境是uni-app,主要测试用机型为红米Note7(前辈走前留下来的,自己的手机是华为,折腾了半天也连不上HbuilderX的调试,
小米大胜利),除非uni插件无法实现的功能(例如开关蓝牙等操作)否则大部分使用uni自身封装的内容,尽量少使用native.js - 主要通信模式是通过蓝牙,发送命令使用ASCII协议来调用设备的部分函数
00 前置准备
工利善其事必先利其器,本人读书时学的是土木工程,曾经实习的时候接触和使用过全站仪,知道部分基础的使用方法,所以学起来还算比较快,这个网上相关的文档链接也不少,这里随便贴一个地址:网址链接
然后是页面的设计了,因为所在的小团队没有专门的设计师,只能由我一个苦逼的工科男来设计页面,参考一些比较好看的app来复制一下别人的样式,不过这个问题也不大,uni的插件市场的几个插件做的还算可以的。
01 蓝牙功能的使用
本来是打算直接使用uni的蓝牙功能的,结果uni封装的只支持BLE蓝牙,徕卡全站仪的蓝牙也搜不到(搜出来有很多名称为空的设备,也不知道是不是机型的问题)
而且uni自身貌似不支持控制蓝牙的开关,因此我就直接将前辈开发的另一个H5+App中蓝牙模块的内容移植到我自己的项目,也成功能搜寻到大部分设备了。
值得庆幸的是,蓝牙功能模块本身没有什么大问题,唯一的问题是H5+App中前辈在封装的对象中定义了部分windows和document类,主要减少重复开关蓝牙页面的时候创建重复的BlueAdapter实例和操作部分dom元素使用的:将新创建的实例放在windwos下,构造的时候优先返回windows下的实例对象。我把这个功能封装在app初始化的时候,放在vuex里面了。
02 徕卡全站仪开发手册
这个CSDN上也有相关的资源,我这边使用的相关手册和工具是另一个前辈通过徕卡官方申请的,在这里我就不贴相关内容和链接了,这个还算比较好找。
然后坑爹的地方就来了,在开发手册里面有这么一行例子:
该命令是一个查询时间的命令,发送该命令后会返回设备中时间的相关参数。我复制过来后贴在蓝牙的发送数据里面,连接后自动发送该命令,先尝试读取回收的数据。
//链接成功后的回调
deviceConnectedCallback: (address) => {
this.allDevices.find(item => item.address == address).status = 'connected';
setTimeout(() => {
blueToothTool.sendData(`%R1Q,5008:1^m`);
}, 5000)
},
//读取数据的回调
readDataCallback: (dataByteArr) => {
console.log(dataByteArr);
}
然后直接就拿不到数据,控制台也没有报错。然后只能跑去分析前辈的源码。
/**
* 模拟java多线程读取数据
*/
function read(msgCount) {
//clearInterval(setIntervalId);
let dataArr = [];
let hasdata = false;
let datacount = 0;
let block = false;
let reading = false;
//setIntervalId = setInterval(doReadData, 100);
var mcount = 0;
let start = new Date().getTime();
let last = start;
while ((new Date().getTime()) - start < 3000) {
let now = new Date().getTime();
if (now - last > 200) {
last = now;
let bres = doReadData();
if (bres) {
mcount++;
if (mcount == msgCount) {
break;
}
}
}
}
function doReadData() {
//console.log('模拟线程:'+t);
//setTimeCount++;
if (hasdata) return false;
if (block) return false;
while (invoke(btInStream, "available") != 0) {
hasdata = true;
let data = invoke(btInStream, "read");
dataArr.push(data);
}
if (hasdata && invoke(btInStream, "available") == 0) {
block = true;
hasdata = false;
//var strResponse = String.fromCharCode.apply(String, dataArr);
//strResponse = strResponse.replace('\r','').replace('\n',';');
//console.log('输出结果_E:' + strResponse);
options.readDataCallback && options.readDataCallback(dataArr);
dataArr = [];
block = false;
return true;
}
return false;
}
}
/**
* 发送数据
* @param {String} dataStr
* @return {Boolean}
*/
function sendData(dataStr) {
try {
let bytes = invoke(dataStr, 'getBytes','Ascii');
btOutStream.write(bytes);
console.log('发送数据:' + dataStr);
read(2);
} catch(e) {
alert('发送失败!'+JSON.stringify(e));
return false;
}
return true;
}
看了半天也没看出个所以然,读取数据中doReadData方法里的invoke(btInStream, “available”)始终为0,但是sendData的控制台也能成功输出数据。于是自己写了个测试程序,用两台手机做测试发现两边都能发送和接受,说明这个模块本身是没有问题的。
后来发现一款APP,测量员。它能够通过蓝牙成功链接和操作我们单位采购的设备,这也说明采购的设备支持geocom开发,而且蓝牙确实是可以传输数据的。其实在这里不难想到直接用蓝牙数据抓包就可以解决问题了,但是我在这里一直纠结是不是我自己写的方法有问题,或者是因为机型的适配性问题,在这里浪费了大量的时间。
在尝试很多方法之后还是选择使用抓包,在手机设置中,开发人员选项里设置蓝牙数据抓取,小米的手机还不能直接通过adb shell后的命令找到指定的log文件,不过还好还是有方法拿到指定的文件的:网址链接
抓到数据的那一刻我就傻眼了,原来我之前做的工作90%的是对的,只是少了一个东西
这个是别人的APP:
这个是我的:
然后我才理解到,原来蓝牙输入到设备的数据可能中类似于cmd中的输入内容,需要敲一下回车才能执行命令,原来我之前几天的工作全部木大
拿到回执的数据了!