yangyongkai 3 rokov pred
commit
f507ef61b9

+ 0 - 0
.gitignore


+ 1 - 0
README.md

@@ -0,0 +1 @@
+OBD微信小程序

+ 51 - 0
app.js

@@ -0,0 +1,51 @@
+App({
+  buf2hex: function (buffer) {
+    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('')
+  },
+  buf2string: function (buffer) {
+    var arr = Array.prototype.map.call(new Uint8Array(buffer), x => x)
+    var str = ''
+    for (var i = 0; i < arr.length; i++) {
+      str += String.fromCharCode(arr[i])
+    }
+    return str
+  },
+  ab2hex: function (buffer) {
+    var hexArr = Array.prototype.map.call(
+      new Uint8Array(buffer),
+      function (bit) {
+        return ('00' + bit.toString(16)).slice(-2)
+      }
+    )
+    return hexArr.join('');
+  },
+  strToHexCharCode: function (str) {
+    if (str === "")
+      return "";
+    var hexCharCode = [];
+    hexCharCode.push("0x");
+    for (var i = 0; i < str.length; i++) {
+      hexCharCode.push((str.charCodeAt(i)).toString(16));
+    }
+    return hexCharCode.join("");
+  },
+  stringToBytes: function (str) {
+    var array = new Uint8Array(str.length);
+    for (var i = 0, l = str.length; i < l; i++) {
+      array[i] = str.charCodeAt(i);
+    }
+    return array.buffer;
+  },
+  stringToHexBuffer: function (str) {
+    var typedArray = new Uint8Array(str.match(/[\da-f]{2}/gi).map(function (h) {
+    return parseInt(h, 16)
+    }))
+    return typedArray.buffer
+    },
+  onLaunch: function () {
+    this.globalData.SystemInfo = wx.getSystemInfoSync()
+  },
+  globalData: {
+    SystemInfo: {}
+  }
+})

+ 14 - 0
app.json

@@ -0,0 +1,14 @@
+{
+  "pages": [
+    "pages/search/search",
+    "pages/device/device"
+  ],
+  "window": {
+    "backgroundTextStyle": "light",
+    "navigationBarBackgroundColor": "#f8f8f8",
+    "navigationBarTitleText": "OBD TOOL",
+    "navigationBarTextStyle": "black",
+    "backgroundColor": "#f8f8f8"
+  },
+  "sitemapLocation": "sitemap.json"
+}

+ 10 - 0
app.wxss

@@ -0,0 +1,10 @@
+/**app.wxss**/
+.container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: space-between;
+  padding: 200rpx 0;
+  box-sizing: border-box;
+} 

+ 262 - 0
assets/js/md5.js

@@ -0,0 +1,262 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}
+
+
+module.exports =  {
+  hexMD5: hex_md5,
+  b64Md5: b64_md5,
+} 

BIN
images/bluetooth.png


+ 400 - 0
pages/device/device.js

@@ -0,0 +1,400 @@
+const data_trans = getApp()
+const utils_base64 = require('../../utils/util.js')
+const websocket_api = require('../../utils/socket_api.js')
+const get_date_api = require('../../utils/getDate.js')
+var initData = '以下为操作结果:'
+var extraLine = [];
+
+Page({
+  data: {
+    log_text: initData,
+    inputText: '',
+    receiveText: '',
+    log_text_area: '',
+    connectedDeviceId: '',
+    services: {},
+    characteristics: {},
+    name:'',
+    advertisData:'',
+    connected: true,
+    salt:'',
+    //下拉框相关的.
+    display: 'none',
+    proShow: false,
+    proindex: 0, //255
+    proData: [],
+    phyAddrInfoLIst: [], //保存物理地址信息,包括中文名,英文名,枚举值
+    claShow: false,
+    claindex: 0, //255
+    claData: [],
+    didNameInfoList:[], //保存did信息,包括中文名,英文名
+    },
+    add: function (e) {
+      extraLine.push(e)
+      this.setData({
+        log_text_area: initData + '\n' + extraLine.join('\n')
+      })
+    },
+    remove: function (e) {
+      if (extraLine.length > 0) {
+        extraLine.pop(e)
+        this.setData({
+          log_text_area: initData + '\n' + extraLine.join('\n')
+        })
+      }
+    },
+    // 点击下拉专业显示框
+    selectCom() {
+      var that = this;
+      console.log(that.data.proShow)
+      if (that.data.proShow) {
+        that.setData({
+          display: 'none'
+        })
+      } else {
+        that.setData({
+          display: 'block',
+        })
+      }
+      that.setData({
+        proShow: !that.data.proShow,
+        claShow: false
+      })
+    },
+
+  //点击选择部件
+  optionUnit(e) {
+    var that = this;
+    var phyAddrIndex = e.currentTarget.dataset.proindex;
+    console.log("phyAddrIndex:", phyAddrIndex)
+    console.log("name_cn:", that.data.phyAddrInfoLIst.args[phyAddrIndex].name_cn)
+    console.log("enum_value:", that.data.phyAddrInfoLIst.args[phyAddrIndex].enum_value)
+
+    that.setData({
+      proindex: phyAddrIndex,
+      proShow: !that.data.proShow,
+      display: 'none',
+    })
+    wx.request({
+      url: 'http://www.id27149.com:2222/codecInfo',//服务器接口地址
+      method:'POST',//请求类型
+      header: {'content-type': 'application/json'},
+      data: {'method':'get_read_did_name_list','args':["BMW_790", that.data.phyAddrInfoLIst.args[phyAddrIndex].enum_value]},
+      success: function(res) {
+        console.log('请求did列表成功,res:', res.data)
+        //把请求到的原始数据放到phyAddrInfoLIst里
+        that.setData({
+          didNameInfoList: res.data
+        })
+        //把请求到的部件名称放到下拉框列表里面
+        var did_list = []
+        for (var i = 0; i < res.data.args.length; i++) {
+          did_list[i] = res.data.args[i][1]
+        }
+        that.setData({
+          claData: did_list
+        })
+      },
+      fail: function() {
+        console.log('请求did列表失败')
+      }
+    })
+  },
+
+  //点击下拉框:功能支持情况
+  selectFeature() {
+    var that = this;
+    if (that.data.claShow) {
+      that.setData({
+        display: 'none',
+      })
+    } else {
+      that.setData({
+        display: 'block',
+      })
+    }
+    that.setData({
+      claShow: !that.data.claShow,
+			proShow: false
+    })
+  },
+
+  optionFeature(e) {
+    var that = this;
+    var claindex = e.currentTarget.dataset.claindex;
+    console.log("optionFeature:", claindex)
+
+    that.setData({
+      claindex: claindex,
+      claShow: !that.data.claShow,
+      display: 'none'
+    })
+  },
+
+	//隐藏遮罩层
+	hiddenShadow:function(){
+		this.setData({
+			display:'none',
+			proShow:false,
+			claShow:false
+		})
+  },
+  
+  GetSelectListInfo:function(){
+    var that = this;
+    console.log("按下获取下拉框选择情况按钮")
+    if(that.data.proData != null){
+      console.log("当前选择物理地址情况如下:")
+      console.log("proindex:", that.data.proindex)
+      console.log("name_cn:",  that.data.phyAddrInfoLIst.args[that.data.proindex].name_cn)
+      console.log("enum_value:", that.data.phyAddrInfoLIst.args[that.data.proindex].enum_value)
+    }else{
+      console.log("部件下拉列表框更新失败")
+    }
+
+    if(that.data.claData != null){
+      console.log("当前选择did情况如下:")
+      console.log("optionFeature:", that.data.claindex)
+      console.log("name_en:",  that.data.didNameInfoList.args[that.data.claindex][0])
+      console.log("name_ch:", that.data.didNameInfoList.args[that.data.claindex][1])
+    }else{
+      console.log("功能下拉列表框更新失败")
+    }
+
+    console.log("点击获取信息按钮,向服务器请求did")
+    var phy_enum_val = that.data.phyAddrInfoLIst.args[that.data.proindex].enum_value
+    var did_name = that.data.didNameInfoList.args[that.data.claindex][0]
+    websocket_api.SocketTxMsg(
+        JSON.stringify({
+          "method": "read_did",
+          "args": {'N_TA':phy_enum_val,'did_list':[did_name]}
+        }),
+        this.WebsocketSendSuccessEvent, 
+        this.WebsocketSendFailEvent
+      )
+  },
+
+  //获取文字输入框内容
+  bindInput: function (e) {
+    this.setData({
+      inputText: e.detail.value
+    })
+    console.log(e.detail.value)
+  },
+
+  WebsocketSendSuccessEvent:function (res) {
+    console.log("socket发送成功,res:",res)
+  },
+
+  WebsocketSendFailEvent:function (res) {
+    console.log("socket发送失败,res:", res)
+  },
+
+  // // 获取设备生产日期
+  // GetManufactureDate: function()
+  // {
+  //   console.log("点击获取生产日期按钮,向服务器请求did")
+  //   websocket_api.SocketTxMsg(
+  //       JSON.stringify({
+  //         "method": "read_did",
+  //         "args": {'N_TA':18,'did_list':['UDS_MANUFACTURING_DATA']}
+  //       }),
+  //       this.WebsocketSendSuccessEvent, 
+  //       this.WebsocketSendFailEvent
+  //     )
+  // },
+  // // 获取设备制造厂商
+  // GetManufacture: function()
+  // {
+  //   var data = "521214"
+  //   // this.setData({inputText: "521214"})
+  //   this.SendBleData(data)
+  // },
+
+  //发送指令
+  SendBleData: function (str) {
+    var that = this
+    //正常的下发蓝牙数据
+    if (that.data.connected) {
+      // 把string转换为hex后进行发送
+      var buffer = data_trans.stringToHexBuffer(str)
+      wx.writeBLECharacteristicValue({
+        deviceId: that.data.connectedDeviceId,
+        serviceId: '0000FEE7-0000-1000-8000-00805F9B34FB',
+        characteristicId: '000036F5-0000-1000-8000-00805F9B34FB',
+        value: buffer,
+        success: function (res) {
+          console.log('发送成功')
+        }
+      })
+    }
+    else {
+      wx.showModal({
+        title: '提示',
+        content: '蓝牙已断开',
+        showCancel: false,
+        success: function (res) {
+          that.setData({
+            searching: false
+          })
+        }
+      })
+    }
+  },
+
+  // //点击发送按钮发送指令
+  // SendButton: function () {
+  //   var that = this
+  //   console.log("发送蓝牙数据:", that.data.inputText)
+  //   this.SendBleData(that.data.inputText)
+  // },
+
+  onSocketMessageCallback:function(res){
+    console.log("收到服务器消息:res", res);
+    if(res == "heartbeat") {
+      console.log("rx server heartbeat")
+    } else {
+      //字符串转为字典.
+      const dict = JSON.parse(res)
+      if(dict['method'] == "send_data"){
+        console.log("将数据通过蓝牙发送至设备,", dict['args'])
+        this.SendBleData(dict['args'])
+      } else if(dict['method'] == "task_result") {
+        //显示到界面上
+        var that = this
+        // that.setData({receiveText: JSON.stringify(dict['args'])})
+        var date = new Date()
+        // var nowtime = util.formatTime(date);
+        this.add(get_date_api.formatTime(date) + ":" + JSON.stringify(dict['args']))
+        // this.add(JSON.stringify(dict['args']))
+        // that.setData({log_text_area: JSON.stringify(dict['args'])})
+      }
+    }
+  },
+
+  onLoad: function (options) {
+    var that = this
+    console.log(options)
+    //刚进入界面时请求下下拉框列表
+    wx.request({
+      url: 'http://www.id27149.com:2222/codecInfo',//更新下拉框内容服务器接口地址
+      method:'POST',//请求类型
+      header: {'content-type': 'application/json'},
+      data: {'method':'get_device_phy_value_list','args':["BMW_790"]},
+      success: function(res) {
+        console.log('请求物理列表成功,res:', res.data)
+        //把请求到的原始数据放到phyAddrInfoLIst里
+        that.setData({
+          phyAddrInfoLIst: res.data
+        })
+        //把请求到的部件名称放到下拉框列表里面
+        var phy_list = []
+        for (var i = 0; i < res.data.args.length; i++) {
+          phy_list[i] = res.data.args[i].name_cn
+        }
+        that.setData({
+          proData: phy_list
+        })
+        console.log("当前选中的物理列表index:", that.data.proindex)
+        console.log("当前选中的物理value:", that.data.phyAddrInfoLIst.args[that.data.proindex].enum_value)
+        //紧接着请求当前物理部件的did list
+        wx.request({
+          url: 'http://www.id27149.com:2222/codecInfo',//服务器接口地址
+          method:'POST',//请求类型
+          header: {'content-type': 'application/json'},
+          data: {'method':'get_read_did_name_list','args':["BMW_790", that.data.phyAddrInfoLIst.args[that.data.proindex].enum_value]},
+          success: function(res) {
+            console.log('请求did列表成功,res:', res.data)
+            //把请求到的原始数据放到phyAddrInfoLIst里
+            that.setData({
+              didNameInfoList: res.data
+            })
+            //把请求到的部件名称放到下拉框列表里面
+            var did_list = []
+            for (var i = 0; i < res.data.args.length; i++) {
+              did_list[i] = res.data.args[i][1]
+            }
+            that.setData({
+              claData: did_list
+            })
+          },
+          fail: function() {
+            console.log('请求did列表失败')
+          }
+        })
+      },
+      fail: function() {
+        console.log('请求物理列表失败')
+      }
+    })
+
+    // 设置socket接收到消息的回调
+    websocket_api.onSocketMessageCallback = this.onSocketMessageCallback;
+    that.setData({
+      connectedDeviceId: options.connectedDeviceId,
+      name:options.name,
+      advertisData:options.advertisData
+    })
+    wx.getBLEDeviceServices({
+      deviceId: that.data.connectedDeviceId,
+      success: function (res) {
+        console.log(res.services)
+        that.setData({
+          services: res.services
+        })
+        wx.getBLEDeviceCharacteristics({
+          deviceId: options.connectedDeviceId,
+          serviceId: res.services[0].uuid,
+          success: function (res) {
+            console.log(res.characteristics)
+            that.setData({
+              characteristics: res.characteristics
+            })
+            wx.notifyBLECharacteristicValueChange({
+              state: true,
+              deviceId: options.connectedDeviceId,
+              serviceId: '0000FEE7-0000-1000-8000-00805F9B34FB',
+              characteristicId: '000036F6-0000-1000-8000-00805F9B34FB',
+              success: function (res) {
+                console.log('启用notify成功')
+              }
+            })
+          }
+        })
+      }
+    })
+    wx.onBLEConnectionStateChange(function (res) {
+      console.log(res.connected)
+      that.setData({
+        connected: res.connected
+      })
+    })
+    
+    //注册接收到蓝牙消息之后的回调函数
+    //蓝牙设备发送的消息
+    wx.onBLECharacteristicValueChange(function (res) {
+      var receiveText = data_trans.ab2hex(res.value)
+      console.log('接收到以下蓝牙数据透传至服务器:' + receiveText)
+      //发送至服务器,
+      websocket_api.SocketTxMsg(
+        JSON.stringify({"method": "data","args": receiveText}),
+        this.WebsocketSendSuccessEvent, 
+        this.WebsocketSendFailEvent
+      )
+      // //显示到界面上
+      // that.setData({receiveText: receiveText})
+    })
+  },
+  
+  onReady: function () {
+
+  },
+
+  onShow: function () {
+
+  },
+
+  onHide: function () {
+    console.log('hide')
+  }
+})

+ 1 - 0
pages/device/device.json

@@ -0,0 +1 @@
+{}

+ 63 - 0
pages/device/device.wxml

@@ -0,0 +1,63 @@
+<!-- <view class="container">
+  <text style="font-size:medium;word-break:break-all">设备名称:{{name}}</text>
+  <text style="font-size:x-small;color:gray;word-break:break-all">设备ID:{{connectedDeviceId}}</text>
+  <text style="font-size:x-small;color:gray">状态:{{connected?"已连接":"已断开"}}</text> -->
+  <!-- <view class="layout_horizontal"> -->
+      <!-- <button type="primary" class="button" bindtap="GetManufactureDate">查看生产日期</button>
+      <button type="primary" class="button" bindtap="GetManufacture">查看制造厂商</button> -->
+  <!-- </view> -->
+    <!-- <view class="layout_horizontal">
+      <button type="primary" class="button" bindtap="SendOpen">打开电池</button>
+      <button type="primary" class="button" bindtap="SendClose">关闭电池</button>
+  </view> -->
+    <!-- <text style="font-size:medium;margin-top:10px">发送内容:</text>
+    <input class="input" value="{{inputText}}" bindinput="bindInput" maxlength="50"/>
+    <text style="font-size:medium;margin-top:10px">接收内容:</text>
+    <input class="input" disabled value="{{receiveText}}" maxlength="50"/>
+    <!-- <button type="primary" class="button" bindtap="SendButton">发送</button> -->
+<!-- </view> -->
+
+<view catchtap='hiddenShadow'>
+  <view class='head'>
+    <view class='selectBox'>
+      <!-- 第一栏选择框标题位置 -->
+      <view class='select' catchtap='selectCom'>
+        <text class='select_text' style='color:{{proShow?"#008bff":"#4A4A4A"}}'>{{proData[proindex]}}</text>
+      </view>
+      <!-- 第一栏选择框底部可选择部分 -->
+      <view class='option_box' style='height:{{proShow?(proData.length>3?210:proData.length*70):0}}rpx'>
+        <text class='option' style='color:{{index==proindex?"#008bff":"#4A4A4A"}}' wx:for='{{proData}}' wx:key='*this' data-proindex='{{index}}' catchtap='optionUnit'>{{item}}</text>
+      </view>
+			<!-- 第二栏选择框标题位置 -->
+      <view class='select' catchtap='selectFeature'>
+        <text class='select_text' style='color:{{claShow?"#008bff":"#4A4A4A"}}'>{{claData[claindex]}}</text>
+      </view>
+      <!-- 第二栏选择框底部可选择部分 -->
+      <view class='option_box' style='height:{{claShow?(claData.length>3?210:claData.length*70):0}}rpx'>
+        <text class='option' style='color:{{index==claindex?"#008bff":"#4A4A4A"}}' wx:for='{{claData}}' wx:key='*this' data-claindex='{{index}}' catchtap='optionFeature'>{{item}}</text>
+      </view>
+    </view>
+
+    <view class='option_box' 
+    style='height:{{claShow?(claData.length>3?210:claData.length*70):0}}rpx'>
+        <text class='option' style='color:{{index==claindex?"#008bff":"#4A4A4A"}}' wx:for='{{claData}}' wx:key='*this' data-claindex='{{index}}' catchtap='optionFeature'>{{item}}</text>
+    </view>
+  </view>
+  <!-- 下拉框遮罩层 -->
+  <view class='shadow' style='display:{{display}}' catchtouchmove='true'></view>
+
+
+  <view class='button_box'>
+    <button type="primary" class="button"  bindtap="GetSelectListInfo">{{"获取以上信息"}}</button>
+  </view>
+
+  <view class='log_area'>
+    <textarea placeholder='日志显示区域' bindinput="getWords" disabled value="{{log_text_area}}" maxlength='-1'></textarea>
+  </view>
+
+    <!-- <text style="font-size:medium;margin-top:10px">接收内容:</text> -->
+    <!-- <text> {{log_text}}</text> -->
+    <!-- <textarea placeholder='日志显示区域' bindinput="getWords" disabled value="{{log_text_area}}" maxlength='-1'></textarea> -->
+    <!-- <input class="input" disabled value="{{receiveText}}" maxlength="20"/> -->
+
+</view> 

+ 97 - 0
pages/device/device.wxss

@@ -0,0 +1,97 @@
+/* page {
+  background-color: #f8f8f8;
+}
+.container {
+  padding: 30rpx;
+  align-items: left;
+}
+.input {
+  margin-top:3px;
+  width: 100%;
+  border: 1px solid lightgray;
+  border-radius: 6px;
+}
+.button {  
+  margin-top:20px;
+  margin-left: 5px;
+  margin-right: 5px;
+}
+.layout_horizontal{
+  height: 100rpx;
+  display: flex;
+  /*row 横向  column 列表  */
+  /* flex-direction: row; */
+/* } */
+
+page {
+  background-color: #f8f8f8;
+  font-size: 32rpx;
+  line-height: 1.6;
+}
+.scrollPage{
+	height: 100%;
+	overflow: hidden;
+}
+.searchBar{
+  background: #fff;
+  height: 72rpx;
+  padding: 0 30rpx 16rpx;
+}
+.searchBar >input {
+  background: #f0f1f2;
+  height: 56rpx;
+  border-radius: 28rpx;
+  text-align: center;
+}
+.selectBox{
+	background: #fff;
+  margin-top: 18rpx;
+  position: relative;
+  display: flex;
+}
+.select{
+	box-sizing: border-box;
+  width: 50%;
+  height: 100rpx;
+  line-height: 100rpx;
+  border: 1rpx solid #efefef;
+  text-align: center;
+}
+.select_text{
+	font-size: 30rpx;
+  color: #4a4a4a;
+}
+.option_box{
+	position: absolute;
+  top: 100rpx;
+  width: 100%;
+  background: #fff;
+  font-size: 30rpx;
+  padding-left: 40rpx;
+  overflow-y: auto;
+  transition: height 0.3s;
+  z-index: 2;
+}
+.option{
+	display: block;
+  padding: 12rpx;
+}
+.shadow {
+  width: 100%;
+  height: 100%;
+  background-color: rgba(178, 178, 178, 0.3);
+  z-index: 1;
+  top: 210rpx;
+  position: absolute;
+}
+.log_area {
+  position: relative;
+  background: #e4e4e6;
+  padding: 26rpx;
+  border-radius: 12rpx;
+}
+.log_area >textarea{
+  width:100%;
+  height:860rpx;
+  font-size: 28rpx;
+}

+ 471 - 0
pages/search/search.js

@@ -0,0 +1,471 @@
+const app = getApp()
+const request_api = require('../../utils/api.js')
+const websocket_api = require('../../utils/socket_api.js')
+
+Page({
+  data: {
+    inputText: '',
+    qrcodeMac: "",
+    searching: false,
+    devicesList: []
+  },
+  MacInput: function (e) {
+    this.setData({
+      inputText: e.detail.value
+    })
+    if(e.detail.value.length == 12){
+      console.log("mac addr:", e.detail.value)
+    }
+  },
+
+  ConnectByID: function(targetID){
+    var that=this  
+    console.log("ConnectByID:",targetID)
+    var name,advertisData
+    //获取当前的name和ad
+    for (var i = 0; i < that.data.devicesList.length; i++) {
+      if (targetID == that.data.devicesList[i].deviceId) {
+        name=that.data.devicesList[i].name
+        advertisData = that.data.devicesList[i].advertisData.substring(2)
+        break
+      }
+    }
+
+    console.log("connect:"+targetID+"|"+name+"|"+advertisData)
+    wx.stopBluetoothDevicesDiscovery({
+      success: function (res) {
+        console.log(res)
+      }
+    })
+    wx.showLoading({
+      title: '连接蓝牙设备中...',
+    })
+    wx.createBLEConnection({
+      deviceId: targetID,
+      success: function (res) {
+        console.log(res)
+        wx.hideLoading()
+        wx.showToast({
+          title: '连接成功',
+          icon: 'success',
+          duration: 1000
+        })
+        console.log("getplatform")
+        var platform = wx.getSystemInfoSync().platform
+        console.log(platform)
+        if(platform == "android"){          
+          wx.navigateTo({
+            url: '../device/device?connectedDeviceId=' + targetID +'&name='+name+'&advertisData='+advertisData
+          })
+        }
+        else{
+          console.log('ios device')
+          wx.getBLEDeviceServices({
+            // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+            deviceId: targetID,
+            success: function (res) {
+              console.log("getBLEDeviceServices success")
+              console.log(JSON.stringify(res))
+              //获取设备特征对象
+              wx.getBLEDeviceCharacteristics({
+                deviceId: targetID,
+                serviceId: '0000FEE7-0000-1000-8000-00805F9B34FB',
+                success: function(res) {
+                  console.log('../device/device?connectedDeviceId=' + targetID +'&name='+name+'&advertisData='+advertisData)
+                  wx.navigateTo({
+                    url: '../device/device?connectedDeviceId=' + targetID +'&name='+name+'&advertisData='+advertisData
+                  })
+                },
+                fail:function(){
+                  wx.showModal({
+                    title: '温馨提示',
+                    content: '获取特征对象失败!',
+                    showCancel: false
+                  });
+                  quit(obj);
+                }
+              })
+            },
+            fail:function(){
+              wx.showModal({
+                title: '温馨提示',
+                content: '获取服务失败!',
+                showCancel:false
+              });
+              quit(obj);
+            }
+          })
+        }
+      },
+      fail: function (res) {
+        console.log(res)
+        wx.hideLoading()
+        wx.showModal({
+          title: '提示',
+          content: '连接失败',
+          showCancel: false
+        })
+      }
+    })
+  },
+
+  request_success_callback(res){
+    //http request成功的回调
+    console.log("http request success callback, data:", res.data)
+  },
+  request_fail_callback(fail_code){
+    //http request失败的回调
+    console.log("http request fail callback,fail_code:", fail_code)
+  },
+
+  request_api_test(){
+    //使用api进行http request测试
+    request_api.request('http://www.id27149.com:2222/codecInfo', 'POST', {'method':'get_device_phy_value_list','args':["BMW_790"]}, this.request_success_callback, this.request_fail_callback)
+  },
+
+  request_test() {
+    //使用wx自带的sdk进行http request测试
+    wx.request({
+      url: 'http://www.yykvz.cn',//服务器接口地址
+      method:'GET',//请求类型
+      header: {
+        'content-type': 'application/json'
+      },
+      success:function(res) {
+        console.log(res)
+      },
+      fail:function(err) {
+        console.log(err.errno)
+      }
+    })
+  },
+
+  RequestTest: function(){
+    // 点击网络请求测试按钮事件  
+    console.log("http request测试")
+    // http request测试.
+    this.request_api_test()
+  },
+
+  WebsocketConnSuccessEvent:function (res) {
+    console.log("socket连接成功,res:",res)
+  },
+
+  WebsocketConnFailEvent:function (res) {
+    console.log("socket连接失败,res:", res)
+  },
+
+  WebsocketCloseSuccessEvent:function (res) {
+    console.log("socket连接关闭成功,res:",res)
+  },
+
+  WebsocketCloseFailEvent:function (res) {
+    console.log("socket连接关闭失败,res:", res)
+  },
+
+  WebsocketConnStart:function () {
+    console.log("启动socket连接")
+    const Host = 'ws://id27149.com:2222/codec'
+    websocket_api.connectSocket(
+        Host, 
+        this.WebsocketConnSuccessEvent, 
+        this.WebsocketConnFailEvent
+      )
+  },
+
+  WebsocketConnButton:function () {
+      // 点击websocket测试按钮事件
+      console.log("websocket建立连接按钮事件")
+      this.WebsocketConnStart()
+  },
+
+  // onSocketMessageCallback:function(res){
+  //   console.log("收到服务器消息:res", res);
+  //   //字符串转为字典.
+  //   if(res == "heartbeat"){
+  //     console.log("rx server heartbear")
+  //   }else{
+  //     const dict = JSON.parse(res)
+  //     if(dict['method'] == "send_data"){
+  //       console.log("将数据通过蓝牙发送至设备,", dict['args'])
+  //     }
+  //   }
+  // },
+
+  WebsocketSendSuccessEvent:function (res) {
+    console.log("socket发送成功,res:",res)
+  },
+
+  WebsocketSendFailEvent:function (res) {
+    console.log("socket发送失败,res:", res)
+  },
+
+  WebsocketSendButton:function () {
+    // 点击websocket发送按钮事件
+    console.log("websocket发送按钮事件")
+    websocket_api.SocketTxMsg(
+        JSON.stringify({
+          "method": "read_did",
+          "args": {'N_TA':18,'did_list':['UDS_MANUFACTURING_DATA']}
+        }),
+        this.WebsocketSendSuccessEvent, 
+        this.WebsocketSendFailEvent
+      )
+},
+
+  SearchClick: function(){
+    var that=this    
+    that.setData({qrcodeMac:""})
+    that.Search()
+  },
+  Search: function () {
+    var that = this
+    console.log("search:",that.data.searching)
+    if (!that.data.searching) {
+      //关闭现有的蓝牙连接
+      wx.closeBluetoothAdapter({
+        complete: function (res) {
+          console.log(res)
+          //打开蓝牙适配
+          wx.openBluetoothAdapter({
+            success: function (res) {
+              console.log(res)
+              wx.getBluetoothAdapterState({
+                success: function (res) {
+                  console.log(res)
+                }
+              })
+              //开始搜索蓝牙设备
+              wx.startBluetoothDevicesDiscovery({
+                allowDuplicatesKey: false,
+                success: function (res) {              
+                  
+                  console.log(res)
+                  that.setData({
+                    searching: true,
+                    devicesList: []
+                  })
+                }
+              })
+            },
+            fail: function (res) {
+              console.log(res)
+              wx.showModal({
+                title: '提示',
+                content: '请检查手机蓝牙是否打开',
+                showCancel: false,
+                success: function (res) {
+                  that.setData({
+                    searching: false
+                  })
+                }
+              })
+            }
+          })
+        }
+      })
+    }
+    else {
+      wx.stopBluetoothDevicesDiscovery({
+        success: function (res) {
+          console.log(res)
+          that.setData({
+            searching: false
+          })
+        }
+      })
+    }
+  },
+
+  //连接上述mac地址蓝牙按钮事件
+  ConnectMacBle:function(){
+    var that = this
+    console.log("开始连接蓝牙,mac地址:", that.data.inputText)
+    if(that.data.inputText.length == 12){
+      that.Search()
+      setTimeout(function() {
+        //连接文本框中输入的mac地址的蓝牙设备
+        that.ConnectByMac(that.data.inputText)
+      }, 1500)
+    }else{
+      console.log("mac addr len error")
+    }
+  },
+
+  //扫描二维码按钮事件
+  ScanQR: function(){
+    var that=this    
+    that.setData({
+      searching: false,
+      qrcodeMac:""
+    })      
+    wx.scanCode({
+      success(res) {
+        that.setData({
+          qrcodeMac:res.result,
+        }) 
+        //扫码成功后,搜索查找指定的蓝牙设备并连接
+        that.Search()
+        setTimeout(function() {
+          that.ConnectByMac(that.data.qrcodeMac)
+        }, 1500)
+      }
+    })
+  },
+
+  ConnectClick: function (e) {
+    var that = this
+    console.log(e.currentTarget.id)
+    that.ConnectByID(e.currentTarget.id)
+  },
+
+  ConnectByMac: function(mac_addr){
+    var that=this
+    console.log("qrcode:",mac_addr," devicelen:",that.data.devicesList.length)
+    var target_advertis_data = "1300"+mac_addr
+    for (var i = 0; i < that.data.devicesList.length; i++) {
+      if (that.data.devicesList[i].advertisData.length>12){  
+        if (target_advertis_data == that.data.devicesList[i].advertisData) {
+          var targetID=that.data.devicesList[i].deviceId
+          //避免短时间内重复连接
+          if(that.data.searching == false)
+            return  
+          that.setData({
+            searching: false,
+            qrcodeMac:""
+          })      
+          that.ConnectByID(targetID)
+          break
+        }
+      }
+    }
+    
+    that.setData({
+      searching: false,
+      qrcodeMac:""
+    })       
+  },
+
+  onLoad: function (options) {
+    var that = this
+    // 打开小程序后即创建socket连接
+    this.WebsocketConnStart();
+    // 设置接收消息回调
+    // websocket_api.onSocketMessageCallback = this.onSocketMessageCallback;
+
+    var list_height = ((app.globalData.SystemInfo.windowHeight - 50) * (750 / app.globalData.SystemInfo.windowWidth)) - 110
+    that.setData({
+      list_height: list_height
+    })
+    wx.onBluetoothAdapterStateChange(function (res) {
+      console.log(res)
+      that.setData({
+        searching: res.discovering
+      })
+      if (!res.available) {
+        that.setData({
+          searching: false
+        })
+      }
+    })
+    wx.onBluetoothDeviceFound(function (devices) {
+      //剔除重复设备,兼容不同设备API的不同返回值
+      var isnotexist = true
+      if (devices.deviceId) {
+        if (devices.advertisData)
+        {
+          devices.advertisData = app.buf2hex(devices.advertisData)
+        }
+        else
+        {
+          devices.advertisData = ''
+        }
+        console.log(devices)
+        for (var i = 0; i < that.data.devicesList.length; i++) {
+          if (devices.deviceId == that.data.devicesList[i].deviceId) {
+            isnotexist = false
+          }
+        }
+        if (isnotexist) {
+          that.data.devicesList.push(devices)
+        }
+      }
+      else if (devices.devices) {
+        if (devices.devices[0].advertisData)
+        {
+          devices.devices[0].advertisData = app.buf2hex(devices.devices[0].advertisData)
+        }
+        else
+        {
+          devices.devices[0].advertisData = ''
+        }
+        console.log(devices.devices[0])
+        for (var i = 0; i < that.data.devicesList.length; i++) {
+          if (devices.devices[0].deviceId == that.data.devicesList[i].deviceId) {
+            isnotexist = false
+          }
+        }
+        if (isnotexist) {
+          that.data.devicesList.push(devices.devices[0])
+        }
+      }
+      else if (devices[0]) {
+        if (devices[0].advertisData)
+        {
+          devices[0].advertisData = app.buf2hex(devices[0].advertisData)
+        }
+        else
+        {
+          devices[0].advertisData = ''
+        }
+        console.log(devices[0])
+        for (var i = 0; i < devices_list.length; i++) {
+          if (devices[0].deviceId == that.data.devicesList[i].deviceId) {
+            isnotexist = false
+          }
+        }
+        if (isnotexist) {
+          that.data.devicesList.push(devices[0])
+        }
+      }
+      that.setData({
+        devicesList: that.data.devicesList
+      })
+    })
+  },
+  
+  onUnload: function(){
+    console.log("页面退出时,关闭socket连接")
+    websocket_api.closeSocket(this.WebsocketCloseSuccessEvent, this.WebsocketCloseFailEvent);
+  },
+
+  onReady: function () {
+  },
+
+  onShow: function () {    
+    if(this.data.qrcodeMac=="")
+    {
+      wx.closeBluetoothAdapter({
+        complete: function (res) {
+        }
+      })
+    }
+  },
+
+  onHide: function () {
+    var that = this
+    that.setData({
+      devicesList: []
+    })
+    if (this.data.searching) {
+      wx.stopBluetoothDevicesDiscovery({
+        success: function (res) {
+          console.log(res)
+          that.setData({
+            searching: false
+          })
+        }
+      })
+    }
+  }
+})

+ 1 - 0
pages/search/search.json

@@ -0,0 +1 @@
+{}

+ 28 - 0
pages/search/search.wxml

@@ -0,0 +1,28 @@
+<view class="container">
+
+  <text style="font-size:larger;margin-top:10px">蓝牙诊断工具</text>
+  <text style="font-size:medium;margin-top:10px">在此输入要连接的蓝牙mac地址(字母使用小写)</text>  
+  <input class="input" value="{{inputText}}" placeholder="在此输入mac地址 "bindinput="MacInput" maxlength="12"/>
+
+  <scroll-view scroll-y style="width:690rpx;height:{{list_height}}rpx">
+    <block wx:for="{{devicesList}}" wx:key="deviceId">
+      <view class="list-item" id="{{item.deviceId}}" bindtap="ConnectClick">
+        <view style="display:flex;flex-direction:column;width:80%">
+          <text style="font-size:medium;word-break:break-all">设备名称: {{item.name}}</text>
+          <text style="font-size:x-small;color:gray;word-break:break-all">设备ID: {{item.deviceId}}</text>
+          <text style="font-size:x-small;color:gray;word-break:break-all">广播: {{item.advertisData}}</text>
+          <text style="font-size:x-small;color:gray;word-break:break-all">信号强度RSSI: {{item.RSSI}}</text>
+        </view>
+        <image style="width:36px;height:36px" mode="aspectFit" src="/images/bluetooth.png"></image>
+      </view>
+    </block>
+  </scroll-view>
+  
+    <!-- 按钮 -->
+    <button type="primary" class="button"  bindtap="ScanQR">{{"扫描二维码"}}</button>
+    <button type="primary" class="button"  bindtap="ConnectMacBle">{{"连接上述mac地址蓝牙"}}</button>
+    <button type="primary" class="button" loading="{{searching}}" bindtap="SearchClick">{{searching?"搜索中...":"搜索蓝牙设备"}}</button>
+    <!-- <button type="primary" class="button"  bindtap="RequestTest">{{"网络请求测试"}}</button> -->
+    <!-- <button type="primary" class="button"  bindtap="WebsocketConnButton">{{"websocket建立连接"}}</button>
+    <button type="primary" class="button"  bindtap="WebsocketSendButton">{{"websocket发送"}}</button> -->
+</view>

+ 27 - 0
pages/search/search.wxss

@@ -0,0 +1,27 @@
+page {
+  background-color: #f8f8f8;
+}
+.container {
+  padding: 0 30rpx 0 30rpx;
+  align-items: center;
+}
+.list-item {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  width: 100%;
+  padding: 10px 0 10px 0;
+  box-sizing: border-box;
+  border: 1px solid #000;
+  border-style: none none solid none;
+  border-bottom-color: lightgray;
+}
+.list-item:last-child {
+  border-style: none;
+}
+.button {
+  height: 80rpx;
+  width: 690rpx;
+  margin: 10rpx;
+}

+ 8 - 0
project.private.config.json

@@ -0,0 +1,8 @@
+{
+  "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
+  "projectname": "OBDWechatBle",
+  "setting": {
+    "compileHotReLoad": true,
+    "urlCheck": false
+  }
+}

+ 7 - 0
sitemap.json

@@ -0,0 +1,7 @@
+{
+  "desc": "ble 示范代码",
+  "rules": [{
+  "action": "allow",
+  "page": "*"
+  }]
+}

+ 41 - 0
utils/api.js

@@ -0,0 +1,41 @@
+const app = getApp();
+let request = (_url, _method, _data, callback, failcallback ) => {
+  wx.showLoading()
+  wx.request({
+    url: _url,
+    method: _method,
+    data: _data,
+    header: {
+      'content-type': 'application/json',
+      'appletId': '',
+      'token': ''
+    },
+    success: ((res) => {
+      wx.hideLoading()
+      console.log(res.statusCode)
+      if ((res.statusCode == 200 || res.statusCode == 201) && callback) {
+        callback(res)
+      }else{
+        if(failcallback){
+          failcallback(res)
+        }else{
+           wx.showToast({
+              title: '服务器开小差',
+              icon: 'none'
+          })
+        }
+      }
+    }),
+    fail: ((res) => {
+      wx.hideLoading()
+      failcallback(res)
+         wx.showToast({
+          title: '服务器开小差',
+          icon: 'none'
+        })
+    })
+  })
+}
+module.exports = {
+  request: request
+}

+ 21 - 0
utils/getDate.js

@@ -0,0 +1,21 @@
+const formatTime = date => {
+  const year = date.getFullYear()
+  const month = date.getMonth() + 1
+  const day = date.getDate()
+  const hour = date.getHours()
+  const minute = date.getMinutes()
+  const second = date.getSeconds()
+
+  // return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
+  return [hour, minute, second].map(formatNumber).join(':')
+
+}
+
+const formatNumber = n => {
+  n = n.toString()
+  return n[1] ? n : '0' + n
+}
+
+module.exports = {
+  formatTime: formatTime
+}

+ 8 - 0
utils/package.json

@@ -0,0 +1,8 @@
+{
+  "name": "wxapp_ajax",
+  "version": "0.0.1",
+  "description": "微信小程序ajax插件",
+  "dependencies": {
+    
+  }
+}

+ 200 - 0
utils/socket_api.js

@@ -0,0 +1,200 @@
+// 域名地址(项目实地址)
+const Host = 'ws://id27149.com:2222/codec'; //codecserver网站
+ 
+// Socket连接成功
+var socketOpen = false;
+// Socket关闭
+var socketClose = false;
+// 消息队列
+var socketMsgQueue = [];
+ 
+// 判断心跳变量
+var heart = null;
+// 心跳失败次数
+var heartBeatFailCount = 0;
+// 终止心跳
+var heartBeatTimeout = null;
+// 终止重连
+var connectSocketTimeout = null;
+ 
+var webSocket = {
+    // 连接Socket
+    connectSocket:function(sever_url, success_cb, fail_cb) {
+        wx.showLoading({
+            title: '正在请求中',
+            mask: true,
+        });
+        socketOpen = false;
+        socketClose = false;
+        socketMsgQueue = [];
+        wx.connectSocket({
+            url:sever_url,
+            success:function(res) {
+                if (success_cb) {
+                  success_cb(res);
+                }
+            },
+            fail:function(res) {
+                if (fail_cb) {
+                  fail_cb(res);
+                }
+            }
+        })
+    },
+
+    // 发送消息
+    SocketTxMsg:function(msg, success_cb, fail_cb) {
+        if (socketOpen) {
+            wx.sendSocketMessage({
+                data: msg,
+                success: function(res) {
+                    if (success_cb) {
+                      success_cb(res);
+                    }
+                },
+                fail: function(res) {
+                    if (fail_cb) {
+                      fail_cb(res);
+                    }
+                }
+            })
+        } else {
+            socketMsgQueue.push(options.msg)
+        }
+    },
+
+    // 关闭Socket
+    closeSocket: function(success_cb, fail_cb) {
+        if (connectSocketTimeout) {
+            clearTimeout(connectSocketTimeout);
+            connectSocketTimeout = null;
+        };
+        socketClose = true;
+        this.stopHeartBeat();
+        wx.closeSocket({
+            success: function(res) {
+                if (success_cb) {
+                  success_cb(res);
+                }
+            },
+            fail: function(res) {
+                if (fail_cb) {
+                    fail_cb(res);
+                }
+            }
+        })
+    },
+    // 收到消息
+    onSocketMessageCallback: function(msg) {
+      console.log("收到服务器消息:", msg)
+    },
+ 
+    // 开始心跳
+    startHeartBeat: function() {
+        heart = true;
+        this.heartBeat();
+    },
+ 
+    // 正在心跳
+    heartBeat: function() {
+        console.log("启动心跳")
+        // 与后端约定,传点消息,保持链接
+        var heartbeat_data = "123456"
+        var that = this;
+        if (!heart) {
+            return;
+        };
+        if (socketOpen) {
+          wx.sendSocketMessage({
+            data: JSON.stringify({
+              "method": "heartbeat",
+              "args": "1234"
+            }),
+            success: function(res) {
+                console.log("心跳发送成功, res:", res)
+                if (heart) {
+                    heartBeatTimeout = setTimeout(() => {
+                        that.heartBeat();
+                    }, 7000);
+                }
+            },
+            fail: function(res) {
+              console.log("心跳发送失败, res:", res)
+              if (heartBeatFailCount > 2) {
+                  that.connectSocket();
+              };
+              if (heart) {
+                  heartBeatTimeout = setTimeout(() => {
+                      that.heartBeat();
+                  }, 7000);
+              };
+              heartBeatFailCount++;
+            },
+        });
+      }else{
+        // socketMsgQueue.push(heartbeat_data)
+      }
+    },
+ 
+    // 结束心跳
+    stopHeartBeat: function() {
+        console.log("结束心跳")
+        heart = false;
+        if (heartBeatTimeout) {
+            clearTimeout(heartBeatTimeout);
+            heartBeatTimeout = null;
+        };
+        if (connectSocketTimeout) {
+            clearTimeout(connectSocketTimeout);
+            connectSocketTimeout = null;
+        }
+    }
+};
+
+// 监听WebSocket打开连接
+wx.onSocketOpen(function(res) {
+    wx.hideLoading();
+    // 如果已经关闭socket
+    if (socketClose) {
+        webSocket.closeSocket();
+    } else {
+        console.log("打开socket成功")
+        socketOpen = true
+        for (var i = 0; i < socketMsgQueue.length; i++) {
+            // webSocket.SocketTxMsg(socketMsgQueue[i], null, null)
+            wx.sendSocketMessage({
+              data: socketMsgQueue[i],
+              success: function(res) {
+                console.log("send success, res:", res)
+              },
+              fail: function(res) {
+                console.log("send fail, res:", res)
+              }
+          })
+        };
+        socketMsgQueue = []
+        // webSocket.startHeartBeat();
+    }
+});
+ 
+// 监听WebSocket错误
+wx.onSocketError(function(res) {
+    console.log('WebSocket连接打开失败,请检查!', res);
+});
+
+// 监听WebSocket接受到服务器的消息
+wx.onSocketMessage(function(res) {
+    webSocket.onSocketMessageCallback(res.data);
+});
+ 
+// 监听WebSocket关闭连接后马上重连
+wx.onSocketClose(function(res) {
+    if (!socketClose) {
+        clearTimeout(connectSocketTimeout);
+        connectSocketTimeout = setTimeout(() => {
+            webSocket.connectSocket();
+        }, 8000);
+    }
+});
+ 
+module.exports = webSocket;

+ 98 - 0
utils/util.js

@@ -0,0 +1,98 @@
+// base64.js
+var base64 = {
+  _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+  encode (str) { // 加密
+    var output = "";
+    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+    var i = 0;
+    str = this._utf16to8(str);
+    while (i < str.length) {
+      chr1 = str.charCodeAt(i++);
+      chr2 = str.charCodeAt(i++);
+      chr3 = str.charCodeAt(i++);
+      enc1 = chr1 >> 2;
+      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+      enc4 = chr3 & 63;
+      if (isNaN(chr2)) {
+        enc3 = enc4 = 64;
+      } else if (isNaN(chr3)) {
+        enc4 = 64;
+      }
+      output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
+    } return output;
+  },
+  decode (input) { // 解密
+    var output = "";
+    var chr1, chr2, chr3;
+    var enc1, enc2, enc3, enc4;
+    var i = 0;
+    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+    while (i < input.length) {
+        enc1 = this._keyStr.indexOf(input.charAt(i++));
+        enc2 = this._keyStr.indexOf(input.charAt(i++));
+        enc3 = this._keyStr.indexOf(input.charAt(i++));
+        enc4 = this._keyStr.indexOf(input.charAt(i++));
+        chr1 = (enc1 << 2) | (enc2 >> 4);
+        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+        chr3 = ((enc3 & 3) << 6) | enc4;
+        output = output + String.fromCharCode(chr1);
+        if (enc3 != 64) {
+            output = output + String.fromCharCode(chr2);
+        }
+        if (enc4 != 64) {
+            output = output + String.fromCharCode(chr3);
+        }
+    } return this._utf8to16(output);
+  },
+  _utf16to8: function(str) {
+    var out, i, len, c;
+    out = "";
+    len = str.length;
+    for(i = 0; i < len; i++) {
+        c = str.charCodeAt(i);
+        if ((c >= 0x0001) && (c <= 0x007F)) {
+            out += str.charAt(i);
+        } else if (c > 0x07FF) {
+            out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
+            out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
+            out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+        } else {
+            out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
+            out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+        }
+    }
+    return out;
+  },
+  _utf8to16 (str) { 
+    var out, i, len, c;
+    var char2, char3;
+    out = "";
+    len = str.length;
+    i = 0;
+    while(i < len) {
+        c = str.charCodeAt(i++);
+        switch(c >> 4)
+        {
+            case 0: case 1: case 2: case 3: case 4: case 5: case 6:case7:
+              out += str.charAt(i-1);
+            break;
+            case 12: case 13:
+              char2 = str.charCodeAt(i++);
+              out += String.fromCharCode(((c & 0x1F) << 6) | (char2&0x3F));
+            break;
+            case 14:
+              char2 = str.charCodeAt(i++);
+              char3 = str.charCodeAt(i++);
+              out += String.fromCharCode(((c & 0x0F) << 12) |
+                ((char2 & 0x3F) << 6) |
+                ((char3 & 0x3F) << 0));
+            break;
+        }
+    } return out;
+  }
+}
+
+/* 暴露函数 */
+module.exports = base64
+