├── pages ├── index │ ├── index.json │ ├── index.wxss │ ├── index.wxml │ └── index.js └── logs │ ├── logs.json │ ├── logs.wxss │ ├── logs.wxml │ └── logs.js ├── img ├── screen1.PNG └── screen2.jpg ├── sitemap.json ├── app.wxss ├── .idea ├── modules.xml ├── bluetoothprinter.iml └── workspace.xml ├── app.json ├── utils └── util.js ├── app.js ├── README.md ├── project.config.json ├── printer ├── printerutil.js ├── printerjobs.js └── commands.js └── miniprogram_npm └── text-encoding └── index.js.map /pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "蓝牙打印" 3 | } -------------------------------------------------------------------------------- /pages/logs/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "查看启动日志" 3 | } -------------------------------------------------------------------------------- /img/screen1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilien1010/wechat-miniprogram-bluetooth-ble-scanner/HEAD/img/screen1.PNG -------------------------------------------------------------------------------- /img/screen2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilien1010/wechat-miniprogram-bluetooth-ble-scanner/HEAD/img/screen2.jpg -------------------------------------------------------------------------------- /pages/logs/logs.wxss: -------------------------------------------------------------------------------- 1 | .log-list { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 40rpx; 5 | } 6 | .log-item { 7 | margin: 10rpx; 8 | } 9 | -------------------------------------------------------------------------------- /sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /pages/logs/logs.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | page { 3 | background-color: #F8F8F8; 4 | height: 100%; 5 | } 6 | 7 | .container { 8 | min-height: 100%; 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | } -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pages/logs/logs.js: -------------------------------------------------------------------------------- 1 | //logs.js 2 | const util = require('../../utils/util.js') 3 | 4 | Page({ 5 | data: { 6 | logs: [] 7 | }, 8 | onLoad: function () { 9 | this.setData({ 10 | logs: (wx.getStorageSync('logs') || []).map(log => { 11 | return util.formatTime(new Date(log)) 12 | }) 13 | }) 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/logs/logs" 5 | ], 6 | "window": { 7 | "backgroundTextStyle": "light", 8 | "navigationBarBackgroundColor": "#fff", 9 | "navigationBarTitleText": "WeChat", 10 | "navigationBarTextStyle": "black" 11 | }, 12 | "sitemapLocation": "sitemap.json" 13 | } -------------------------------------------------------------------------------- /.idea/bluetoothprinter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /utils/util.js: -------------------------------------------------------------------------------- 1 | const formatTime = date => { 2 | const year = date.getFullYear() 3 | const month = date.getMonth() + 1 4 | const day = date.getDate() 5 | const hour = date.getHours() 6 | const minute = date.getMinutes() 7 | const second = date.getSeconds() 8 | 9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 10 | } 11 | 12 | const formatNumber = n => { 13 | n = n.toString() 14 | return n[1] ? n : '0' + n 15 | } 16 | 17 | module.exports = { 18 | formatTime: formatTime 19 | } 20 | -------------------------------------------------------------------------------- /pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | .page-section { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | width: 100%; 6 | box-sizing: border-box; 7 | border-bottom: 2rpx solid #EEE; 8 | padding: 30rpx 0; 9 | } 10 | 11 | .devices-summary { 12 | padding: 10rpx; 13 | font-size: 30rpx; 14 | } 15 | 16 | .device-list { 17 | height: 700rpx; 18 | } 19 | 20 | .device-item { 21 | border-bottom: 1rpx solid #EEE; 22 | padding: 10rpx; 23 | color: #666; 24 | } 25 | 26 | .device-item-hover { 27 | background-color: rgba(0, 0, 0, .1); 28 | } 29 | 30 | .btn-area { 31 | box-sizing: border-box; 32 | width: 100%; 33 | padding: 0 30rpx; 34 | margin: 30rpx 0; 35 | } 36 | 37 | .connected-area { 38 | font-size: 22rpx; 39 | } 40 | 41 | .connected-info {} 42 | 43 | .input-area { 44 | background: #fff; 45 | margin-top: 10rpx; 46 | width: 100%; 47 | } 48 | 49 | .input { 50 | font-size: 28rpx; 51 | height: 2.58823529em; 52 | min-height: 2.58823529em; 53 | line-height: 2.58823529em; 54 | padding: 10rpx; 55 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: function () { 4 | // 展示本地存储能力 5 | var logs = wx.getStorageSync('logs') || [] 6 | logs.unshift(Date.now()) 7 | wx.setStorageSync('logs', logs) 8 | 9 | // 登录 10 | wx.login({ 11 | success: res => { 12 | // 发送 res.code 到后台换取 openId, sessionKey, unionId 13 | } 14 | }) 15 | // 获取用户信息 16 | wx.getSetting({ 17 | success: res => { 18 | if (res.authSetting['scope.userInfo']) { 19 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 20 | wx.getUserInfo({ 21 | success: res => { 22 | // 可以将 res 发送给后台解码出 unionId 23 | this.globalData.userInfo = res.userInfo 24 | 25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 26 | // 所以此处加入 callback 以防止这种情况 27 | if (this.userInfoReadyCallback) { 28 | this.userInfoReadyCallback(res) 29 | } 30 | } 31 | }) 32 | } 33 | } 34 | }) 35 | }, 36 | globalData: { 37 | userInfo: null 38 | } 39 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 小程序蓝牙扫码枪实例 2 | 微信小程序蓝牙打印示例,代码参考[微信小程序示例](https://github.com/benioZhang/miniprogram-bluetoothprinter/)。官方Demo总比网上随便找的强吧。 3 | 4 | * 测试扫码枪:[维融(weirong)LP1无线红光蓝牙扫描器 一维 蓝牙 1100mAh](https://u.jd.com/ZtHT8dm) 5 | * 测试设备:iPhone 11 Pro Max 6 | * 扫码枪设备必须支持 :BLE模式 (无论安卓 还是 iOS 打开扫码枪之后都要进入 BLE模式) 7 | 8 | ### 效果图 9 |
10 | 11 |
12 | 13 | ### 流程 14 | * 初始化蓝牙模块 `wx.openBluetoothAdapter()` 15 | * 搜寻附近的蓝牙外围设备 `wx.startBluetoothDevicesDiscovery()` 16 | * 监听寻找到新设备的事件 `wx.onBluetoothDeviceFound()` 17 | * 连接低功耗蓝牙设备 `wx.createBLEConnection()` 18 | * 获取蓝牙设备服务 `wx.getBLEDeviceServices()` 19 | * 获取蓝牙设备服务的特征值 `wx.getBLEDeviceCharacteristics()` 20 | * 对支持notify 和 indicate的特征执行 `wx.notifyBLECharacteristicValueChange()` 和 `wx.onBLECharacteristicValueChange()` 21 | * 关闭蓝牙模块 `wx.closeBluetoothAdapter()` 22 | 23 | ### 注意点 24 | **1.与蓝牙设备通信很重要的就是找到对应的Characteristic。如何找到这个Characteristic?** 25 | 目前只能一个个去试!!!如果有更好的做法请告诉我。 26 | 27 | 28 | 连接成功之后,在程序的任何地方,做如下监听,都可以实现读取条码 29 | ``` 30 | function ab2str(buf) { 31 | return String.fromCharCode.apply(null, new Uint8Array(buf)); 32 | } 33 | 34 | 35 | wx.onBLECharacteristicValueChange((result) => { 36 | console.log('监听特征值变化',result); 37 | const barcode = ab2str(result.value); 38 | that.setData({barcode}) 39 | }) 40 | ``` 41 | 42 | ### 参考 43 | * [微信小程序API](https://developers.weixin.qq.com/miniprogram/dev/api/) 44 | * [微信小程序示例](https://github.com/wechat-miniprogram/miniprogram-demo) 45 | * [低功耗蓝牙能力](https://developers.weixin.qq.com/community/develop/doc/0008acd004ccd86b37d649ee55b009?highLine=%25E8%2593%259D%25E7%2589%2599) 46 | * [微信小程序 - 蓝牙接口](https://www.jianshu.com/p/d01dbca67461) 47 | * [ESC(POS)打印控制命令](http://www.xmjjdz.com/downloads/manual/cn/ESC(POS)%E6%89%93%E5%8D%B0%E6%8E%A7%E5%88%B6%E5%91%BD%E4%BB%A4.pdf) 48 | * [ESCPOS](https://github.com/song940/node-escpos) 49 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": true, 9 | "enhance": true, 10 | "postcss": true, 11 | "preloadBackgroundData": false, 12 | "minified": true, 13 | "newFeature": true, 14 | "coverView": true, 15 | "nodeModules": true, 16 | "autoAudits": false, 17 | "showShadowRootInWxmlPanel": true, 18 | "scopeDataCheck": false, 19 | "uglifyFileName": false, 20 | "checkInvalidKey": true, 21 | "checkSiteMap": true, 22 | "uploadWithSourceMap": true, 23 | "compileHotReLoad": false, 24 | "lazyloadPlaceholderEnable": false, 25 | "useMultiFrameRuntime": true, 26 | "useApiHook": true, 27 | "useApiHostProcess": true, 28 | "babelSetting": { 29 | "ignore": [], 30 | "disablePlugins": [], 31 | "outputPath": "" 32 | }, 33 | "useIsolateContext": false, 34 | "userConfirmedBundleSwitch": false, 35 | "packNpmManually": false, 36 | "packNpmRelationList": [], 37 | "minifyWXSS": true, 38 | "disableUseStrict": false, 39 | "minifyWXML": true, 40 | "showES6CompileOption": false, 41 | "useCompilerPlugins": false, 42 | "ignoreUploadUnusedFiles": true 43 | }, 44 | "compileType": "miniprogram", 45 | "libVersion": "2.4.0", 46 | "appid": "wxbaa5f6ae19c82ce0", 47 | "projectname": "miniprogram-bluetoothprinter", 48 | "debugOptions": { 49 | "hidedInDevtools": [] 50 | }, 51 | "isGameTourist": false, 52 | "condition": { 53 | "search": { 54 | "list": [] 55 | }, 56 | "conversation": { 57 | "list": [] 58 | }, 59 | "game": { 60 | "currentL": -1, 61 | "list": [] 62 | }, 63 | "miniprogram": { 64 | "list": [] 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /printer/printerutil.js: -------------------------------------------------------------------------------- 1 | // 打印机纸宽58mm,页的宽度384,字符宽度为1,每行最多盛放32个字符 2 | const PAGE_WIDTH = 384; 3 | const MAX_CHAR_COUNT_EACH_LINE = 32; 4 | 5 | /** 6 | * @param str 7 | * @returns {boolean} str是否全是中文 8 | */ 9 | function isChinese(str) { 10 | return /^[\u4e00-\u9fa5]$/.test(str); 11 | } 12 | 13 | /** 14 | * 返回字符串宽度(1个中文=2个英文字符) 15 | * @param str 16 | * @returns {number} 17 | */ 18 | function getStringWidth(str) { 19 | let width = 0; 20 | for (let i = 0, len = str.length; i < len; i++) { 21 | width += isChinese(str.charAt(i)) ? 2 : 1; 22 | } 23 | return width; 24 | } 25 | 26 | /** 27 | * 同一行输出str1, str2,str1居左, str2居右 28 | * @param {string} str1 内容1 29 | * @param {string} str2 内容2 30 | * @param {number} fontWidth 字符宽度 1/2 31 | * @param {string} fillWith str1 str2之间的填充字符 32 | * 33 | */ 34 | function inline(str1, str2, fillWith = ' ', fontWidth = 1) { 35 | const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth; 36 | // 需要填充的字符数量 37 | let fillCount = lineWidth - (getStringWidth(str1) + getStringWidth(str2)) % lineWidth; 38 | let fillStr = new Array(fillCount).fill(fillWith.charAt(0)).join(''); 39 | return str1 + fillStr + str2; 40 | } 41 | 42 | /** 43 | * 用字符填充一整行 44 | * @param {string} fillWith 填充字符 45 | * @param {number} fontWidth 字符宽度 1/2 46 | */ 47 | function fillLine(fillWith = '-', fontWidth = 1) { 48 | const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth; 49 | return new Array(lineWidth).fill(fillWith.charAt(0)).join(''); 50 | } 51 | 52 | /** 53 | * 文字内容居中,左右用字符填充 54 | * @param {string} str 文字内容 55 | * @param {number} fontWidth 字符宽度 1/2 56 | * @param {string} fillWith str1 str2之间的填充字符 57 | */ 58 | function fillAround(str, fillWith = '-', fontWidth = 1) { 59 | const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth; 60 | let strWidth = getStringWidth(str); 61 | // 内容已经超过一行了,没必要填充 62 | if (strWidth >= lineWidth) { 63 | return str; 64 | } 65 | // 需要填充的字符数量 66 | let fillCount = lineWidth - strWidth; 67 | // 左侧填充的字符数量 68 | let leftCount = Math.round(fillCount / 2); 69 | // 两侧的填充字符,需要考虑左边需要填充,右边不需要填充的情况 70 | let fillStr = new Array(leftCount).fill(fillWith.charAt(0)).join(''); 71 | return fillStr + str + fillStr.substr(0, fillCount - leftCount); 72 | } 73 | 74 | module.exports = { 75 | inline: inline, 76 | fillLine: fillLine, 77 | fillAround: fillAround, 78 | }; -------------------------------------------------------------------------------- /pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | module.exports.max = function(n1, n2) { 3 | return Math.max(n1, n2) 4 | } 5 | module.exports.len = function(arr) { 6 | arr = arr || []; 7 | return arr.length; 8 | } 9 | 10 | 11 | 12 | 13 | 14 | 条码:{{item}} 15 | 16 | 17 | 已发现 {{devices.length}} 个外围设备: 18 | 19 | 20 | {{item.name}} 21 | 信号强度: {{item.RSSI}}dBm ({{utils.max(0, item.RSSI + 100)}}%) 22 | UUID: {{item.deviceId}} 23 | Service数量: {{utils.len(item.advertisServiceUUIDs)}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 最近连接的设备 33 | {{lastDevice}} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 已连接到 {{name}} 41 | 42 | 特性UUID: {{item.uuid}} 43 | 特性值: {{item.value}} 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /printer/printerjobs.js: -------------------------------------------------------------------------------- 1 | const commands = require('commands'); 2 | const TextEncoder = require('text-encoding/index').TextEncoder; 3 | 4 | const printerJobs = function () { 5 | this._queue = Array.from(commands.HARDWARE.HW_INIT); 6 | this._encoder = new TextEncoder("gb2312", {NONSTANDARD_allowLegacyEncoding: true}); 7 | this._enqueue = function (cmd) { 8 | this._queue.push.apply(this._queue, cmd); 9 | } 10 | }; 11 | 12 | /** 13 | * 增加打印内容 14 | * @param {string} content 文字内容 15 | */ 16 | printerJobs.prototype.text = function (content) { 17 | if (content) { 18 | let uint8Array = this._encoder.encode(content); 19 | let encoded = Array.from(uint8Array); 20 | this._enqueue(encoded); 21 | } 22 | return this; 23 | }; 24 | 25 | /** 26 | * 打印文字 27 | * @param {string} content 文字内容 28 | */ 29 | printerJobs.prototype.print = function (content) { 30 | this.text(content); 31 | this._enqueue(commands.LF); 32 | return this; 33 | }; 34 | 35 | /** 36 | * 打印文字并换行 37 | * @param {string} content 文字内容 38 | */ 39 | printerJobs.prototype.println = function (content = '') { 40 | return this.print(content + commands.EOL); 41 | }; 42 | 43 | /** 44 | * 设置对齐方式 45 | * @param {string} align 对齐方式 LT/CT/RT 46 | */ 47 | printerJobs.prototype.setAlign = function (align) { 48 | this._enqueue(commands.TEXT_FORMAT['TXT_ALIGN_' + align.toUpperCase()]); 49 | return this; 50 | }; 51 | 52 | /** 53 | * 设置字体 54 | * @param {string} family A/B/C 55 | */ 56 | printerJobs.prototype.setFont = function (family) { 57 | this._enqueue(commands.TEXT_FORMAT['TXT_FONT_' + family.toUpperCase()]); 58 | return this; 59 | }; 60 | 61 | /** 62 | * 设定字体尺寸 63 | * @param {number} width 字体宽度 1~2 64 | * @param {number} height 字体高度 1~2 65 | */ 66 | printerJobs.prototype.setSize = function (width, height) { 67 | if (2 >= width && 2 >= height) { 68 | this._enqueue(commands.TEXT_FORMAT.TXT_NORMAL); 69 | if (2 === width && 2 === height) { 70 | this._enqueue(commands.TEXT_FORMAT.TXT_4SQUARE); 71 | } else if (1 === width && 2 === height) { 72 | this._enqueue(commands.TEXT_FORMAT.TXT_2HEIGHT); 73 | } else if (2 === width && 1 === height) { 74 | this._enqueue(commands.TEXT_FORMAT.TXT_2WIDTH); 75 | } 76 | } 77 | return this; 78 | }; 79 | 80 | /** 81 | * 设定字体是否加粗 82 | * @param {boolean} bold 83 | */ 84 | printerJobs.prototype.setBold = function (bold) { 85 | if (typeof bold !== 'boolean') { 86 | bold = true; 87 | } 88 | this._enqueue(bold ? commands.TEXT_FORMAT.TXT_BOLD_ON : commands.TEXT_FORMAT.TXT_BOLD_OFF); 89 | return this; 90 | }; 91 | 92 | /** 93 | * 设定是否开启下划线 94 | * @param {boolean} underline 95 | */ 96 | printerJobs.prototype.setUnderline = function (underline) { 97 | if (typeof underline !== 'boolean') { 98 | underline = true; 99 | } 100 | this._enqueue(underline ? commands.TEXT_FORMAT.TXT_UNDERL_ON : commands.TEXT_FORMAT.TXT_UNDERL_OFF); 101 | return this; 102 | }; 103 | 104 | /** 105 | * 设置行间距为 n 点行,默认值行间距是 30 点 106 | * @param {number} n 0≤n≤255 107 | */ 108 | printerJobs.prototype.setLineSpacing = function (n) { 109 | if (n === undefined || n === null) { 110 | this._enqueue(commands.LINE_SPACING.LS_DEFAULT); 111 | } else { 112 | this._enqueue(commands.LINE_SPACING.LS_SET); 113 | this._enqueue([n]); 114 | } 115 | return this; 116 | }; 117 | 118 | /** 119 | * 打印空行 120 | * @param {number} n 121 | */ 122 | printerJobs.prototype.lineFeed = function (n = 1) { 123 | return this.print(new Array(n).fill(commands.EOL).join('')); 124 | }; 125 | 126 | /** 127 | * 设置字体颜色,需要打印机支持 128 | * @param {number} color - 0 默认颜色黑色 1 红色 129 | */ 130 | printerJobs.prototype.setColor = function (color) { 131 | this._enqueue(commands.COLOR[color === 1 ? 1 : 0]); 132 | return this; 133 | }; 134 | 135 | /** 136 | * https://support.loyverse.com/hardware/printers/use-the-beeper-in-a-escpos-printers 137 | * 蜂鸣警报,需要打印机支持 138 | * @param {number} n 蜂鸣次数,1-9 139 | * @param {number} t 蜂鸣长短,1-9 140 | */ 141 | printerJobs.prototype.beep = function (n, t) { 142 | this._enqueue(commands.BEEP); 143 | this._enqueue([n, t]); 144 | return this; 145 | }; 146 | 147 | /** 148 | * 清空任务 149 | */ 150 | printerJobs.prototype.clear = function () { 151 | this._queue = Array.from(commands.HARDWARE.HW_INIT); 152 | return this; 153 | }; 154 | 155 | /** 156 | * 返回ArrayBuffer 157 | */ 158 | printerJobs.prototype.buffer = function () { 159 | return new Uint8Array(this._queue).buffer; 160 | }; 161 | 162 | module.exports = printerJobs; -------------------------------------------------------------------------------- /printer/commands.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 修改自https://github.com/song940/node-escpos/blob/master/commands.js 3 | * ESC/POS _ (Constants) 4 | */ 5 | var _ = { 6 | LF: [0x0a], 7 | FS: [0x1c], 8 | FF: [0x0c], 9 | GS: [0x1d], 10 | DLE: [0x10], 11 | EOT: [0x04], 12 | NUL: [0x00], 13 | ESC: [0x1b], 14 | EOL: '\n', 15 | }; 16 | 17 | /** 18 | * [FEED_CONTROL_SEQUENCES Feed control sequences] 19 | * @type {Object} 20 | */ 21 | _.FEED_CONTROL_SEQUENCES = { 22 | CTL_LF: [0x0a], // Print and line feed 23 | CTL_GLF: [0x4a, 0x00], // Print and feed paper (without spaces between lines) 24 | CTL_FF: [0x0c], // Form feed 25 | CTL_CR: [0x0d], // Carriage return 26 | CTL_HT: [0x09], // Horizontal tab 27 | CTL_VT: [0x0b], // Vertical tab 28 | }; 29 | 30 | _.CHARACTER_SPACING = { 31 | CS_DEFAULT: [0x1b, 0x20, 0x00], 32 | CS_SET: [0x1b, 0x20] 33 | }; 34 | 35 | _.LINE_SPACING = { 36 | LS_DEFAULT: [0x1b, 0x32], 37 | LS_SET: [0x1b, 0x33] 38 | }; 39 | 40 | /** 41 | * [HARDWARE Printer hardware] 42 | * @type {Object} 43 | */ 44 | _.HARDWARE = { 45 | HW_INIT: [0x1b, 0x40], // Clear data in buffer and reset modes 46 | HW_SELECT: [0x1b, 0x3d, 0x01], // Printer select 47 | HW_RESET: [0x1b, 0x3f, 0x0a, 0x00], // Reset printer hardware 48 | }; 49 | 50 | /** 51 | * [CASH_DRAWER Cash Drawer] 52 | * @type {Object} 53 | */ 54 | _.CASH_DRAWER = { 55 | CD_KICK_2: [0x1b, 0x70, 0x00], // Sends a pulse to pin 2 [] 56 | CD_KICK_5: [0x1b, 0x70, 0x01], // Sends a pulse to pin 5 [] 57 | }; 58 | 59 | /** 60 | * [MARGINS Margins sizes] 61 | * @type {Object} 62 | */ 63 | _.MARGINS = { 64 | BOTTOM: [0x1b, 0x4f], // Fix bottom size 65 | LEFT: [0x1b, 0x6c], // Fix left size 66 | RIGHT: [0x1b, 0x51], // Fix right size 67 | }; 68 | 69 | /** 70 | * [PAPER Paper] 71 | * @type {Object} 72 | */ 73 | _.PAPER = { 74 | PAPER_FULL_CUT: [0x1d, 0x56, 0x00], // Full cut paper 75 | PAPER_PART_CUT: [0x1d, 0x56, 0x01], // Partial cut paper 76 | PAPER_CUT_A: [0x1d, 0x56, 0x41], // Partial cut paper 77 | PAPER_CUT_B: [0x1d, 0x56, 0x42], // Partial cut paper 78 | }; 79 | 80 | /** 81 | * [TEXT_FORMAT Text format] 82 | * @type {Object} 83 | */ 84 | _.TEXT_FORMAT = { 85 | TXT_NORMAL: [0x1b, 0x21, 0x00], // Normal text 86 | TXT_2HEIGHT: [0x1b, 0x21, 0x10], // Double height text 87 | TXT_2WIDTH: [0x1b, 0x21, 0x20], // Double width text 88 | TXT_4SQUARE: [0x1b, 0x21, 0x30], // Double width & height text 89 | 90 | TXT_UNDERL_OFF: [0x1b, 0x2d, 0x00], // Underline font OFF 91 | TXT_UNDERL_ON: [0x1b, 0x2d, 0x01], // Underline font 1-dot ON 92 | TXT_UNDERL2_ON: [0x1b, 0x2d, 0x02], // Underline font 2-dot ON 93 | TXT_BOLD_OFF: [0x1b, 0x45, 0x00], // Bold font OFF 94 | TXT_BOLD_ON: [0x1b, 0x45, 0x01], // Bold font ON 95 | TXT_ITALIC_OFF: [0x1b, 0x35], // Italic font ON 96 | TXT_ITALIC_ON: [0x1b, 0x34], // Italic font ON 97 | 98 | TXT_FONT_A: [0x1b, 0x4d, 0x00], // Font type A 99 | TXT_FONT_B: [0x1b, 0x4d, 0x01], // Font type B 100 | TXT_FONT_C: [0x1b, 0x4d, 0x02], // Font type C 101 | 102 | TXT_ALIGN_LT: [0x1b, 0x61, 0x00], // Left justification 103 | TXT_ALIGN_CT: [0x1b, 0x61, 0x01], // Centering 104 | TXT_ALIGN_RT: [0x1b, 0x61, 0x02], // Right justification 105 | }; 106 | 107 | /** 108 | * [BARCODE_FORMAT Barcode format] 109 | * @type {Object} 110 | */ 111 | _.BARCODE_FORMAT = { 112 | BARCODE_TXT_OFF: [0x1d, 0x48, 0x00], // HRI barcode chars OFF 113 | BARCODE_TXT_ABV: [0x1d, 0x48, 0x01], // HRI barcode chars above 114 | BARCODE_TXT_BLW: [0x1d, 0x48, 0x02], // HRI barcode chars below 115 | BARCODE_TXT_BTH: [0x1d, 0x48, 0x03], // HRI barcode chars both above and below 116 | 117 | BARCODE_FONT_A: [0x1d, 0x66, 0x00], // Font type A for HRI barcode chars 118 | BARCODE_FONT_B: [0x1d, 0x66, 0x01], // Font type B for HRI barcode chars 119 | 120 | BARCODE_HEIGHT: function (height) { // Barcode Height [1-255] 121 | return [0x1d, 0x68, height]; 122 | }, 123 | BARCODE_WIDTH: function (width) { // Barcode Width [2-6] 124 | return [0x1d, 0x77, width]; 125 | }, 126 | BARCODE_HEIGHT_DEFAULT: [0x1d, 0x68, 0x64], // Barcode height default:100 127 | BARCODE_WIDTH_DEFAULT: [0x1d, 0x77, 0x01], // Barcode width default:1 128 | 129 | BARCODE_UPC_A: [0x1d, 0x6b, 0x00], // Barcode type UPC-A 130 | BARCODE_UPC_E: [0x1d, 0x6b, 0x01], // Barcode type UPC-E 131 | BARCODE_EAN13: [0x1d, 0x6b, 0x02], // Barcode type EAN13 132 | BARCODE_EAN8: [0x1d, 0x6b, 0x03], // Barcode type EAN8 133 | BARCODE_CODE39: [0x1d, 0x6b, 0x04], // Barcode type CODE39 134 | BARCODE_ITF: [0x1d, 0x6b, 0x05], // Barcode type ITF 135 | BARCODE_NW7: [0x1d, 0x6b, 0x06], // Barcode type NW7 136 | BARCODE_CODE93: [0x1d, 0x6b, 0x48], // Barcode type CODE93 137 | BARCODE_CODE128: [0x1d, 0x6b, 0x49], // Barcode type CODE128 138 | }; 139 | 140 | /** 141 | * [IMAGE_FORMAT Image format] 142 | * @type {Object} 143 | */ 144 | _.IMAGE_FORMAT = { 145 | S_RASTER_N: [0x1d, 0x76, 0x30, 0x00], // Set raster image normal size 146 | S_RASTER_2W: [0x1d, 0x76, 0x30, 0x01], // Set raster image double width 147 | S_RASTER_2H: [0x1d, 0x76, 0x30, 0x02], // Set raster image double height 148 | S_RASTER_Q: [0x1d, 0x76, 0x30, 0x03], // Set raster image quadruple 149 | }; 150 | 151 | /** 152 | * [BITMAP_FORMAT description] 153 | * @type {Object} 154 | */ 155 | _.BITMAP_FORMAT = { 156 | BITMAP_S8: [0x1b, 0x2a, 0x00], 157 | BITMAP_D8: [0x1b, 0x2a, 0x01], 158 | BITMAP_S24: [0x1b, 0x2a, 0x20], 159 | BITMAP_D24: [0x1b, 0x2a, 0x21] 160 | }; 161 | 162 | /** 163 | * [GSV0_FORMAT description] 164 | * @type {Object} 165 | */ 166 | _.GSV0_FORMAT = { 167 | GSV0_NORMAL: [0x1d, 0x76, 0x30, 0x00], 168 | GSV0_DW: [0x1d, 0x76, 0x30, 0x01], 169 | GSV0_DH: [0x1d, 0x76, 0x30, 0x02], 170 | GSV0_DWDH: [0x1d, 0x76, 0x30, 0x03] 171 | }; 172 | 173 | /** 174 | * [BEEP description] 175 | * @type {string} 176 | */ 177 | _.BEEP = [0x1b, 0x42]; // Printer Buzzer pre hex 178 | 179 | /** 180 | * [COLOR description] 181 | * @type {Object} 182 | */ 183 | 184 | _.COLOR = { 185 | 0: [0x1b, 0x72, 0x00], // black 186 | 1: [0x1b, 0x72, 0x01] // red 187 | }; 188 | 189 | /** 190 | * [exports description] 191 | * @type {[type]} 192 | */ 193 | module.exports = _; -------------------------------------------------------------------------------- /pages/index/index.js: -------------------------------------------------------------------------------- 1 | const LAST_CONNECTED_DEVICE = 'last_connected_device' 2 | const PrinterJobs = require('../../printer/printerjobs') 3 | const printerUtil = require('../../printer/printerutil') 4 | 5 | function inArray(arr, key, val) { 6 | for (let i = 0; i < arr.length; i++) { 7 | if (arr[i][key] === val) { 8 | return i 9 | } 10 | } 11 | return -1 12 | } 13 | 14 | function ab2str(buf) { 15 | return String.fromCharCode.apply(null, new Uint8Array(buf)); 16 | } 17 | 18 | // ArrayBuffer转16进度字符串示例 19 | function ab2hex(buffer) { 20 | const hexArr = Array.prototype.map.call( 21 | new Uint8Array(buffer), 22 | function (bit) { 23 | return ('00' + bit.toString(16)).slice(-2) 24 | } 25 | ) 26 | return hexArr.join(',') 27 | } 28 | 29 | function str2ab(str) { 30 | // Convert str to ArrayBuff and write to printer 31 | let buffer = new ArrayBuffer(str.length) 32 | let dataView = new DataView(buffer) 33 | for (let i = 0; i < str.length; i++) { 34 | dataView.setUint8(i, str.charAt(i).charCodeAt(0)) 35 | } 36 | return buffer; 37 | } 38 | 39 | Page({ 40 | data: { 41 | devices: [], 42 | connected: false, 43 | chs: [], 44 | barcode :[], 45 | }, 46 | onUnload() { 47 | this.closeBluetoothAdapter() 48 | }, 49 | openBluetoothAdapter() { 50 | if (!wx.openBluetoothAdapter) { 51 | wx.showModal({ 52 | title: '提示', 53 | content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。' 54 | }) 55 | return 56 | } 57 | wx.openBluetoothAdapter({ 58 | success: (res) => { 59 | console.log('openBluetoothAdapter success', res) 60 | this.startBluetoothDevicesDiscovery() 61 | }, 62 | fail: (res) => { 63 | console.log('openBluetoothAdapter fail', res) 64 | if (res.errCode === 10001) { 65 | wx.showModal({ 66 | title: '错误', 67 | content: '未找到蓝牙设备, 请打开蓝牙后重试。', 68 | showCancel: false 69 | }) 70 | wx.onBluetoothAdapterStateChange((res) => { 71 | console.log('onBluetoothAdapterStateChange', res) 72 | if (res.available) { 73 | // 取消监听,否则stopBluetoothDevicesDiscovery后仍会继续触发onBluetoothAdapterStateChange, 74 | // 导致再次调用startBluetoothDevicesDiscovery 75 | wx.onBluetoothAdapterStateChange(() => { 76 | }); 77 | this.startBluetoothDevicesDiscovery() 78 | } 79 | }) 80 | } 81 | } 82 | }) 83 | wx.onBLEConnectionStateChange((res) => { 84 | // 该方法回调中可以用于处理连接意外断开等异常情况 85 | console.log('onBLEConnectionStateChange', `device ${res.deviceId} state has changed, connected: ${res.connected}`) 86 | this.setData({ 87 | connected: res.connected 88 | }) 89 | if (!res.connected) { 90 | wx.showModal({ 91 | title: '错误', 92 | content: '蓝牙连接已断开', 93 | showCancel: false 94 | }) 95 | } 96 | }); 97 | }, 98 | getBluetoothAdapterState() { 99 | wx.getBluetoothAdapterState({ 100 | success: (res) => { 101 | console.log('getBluetoothAdapterState', res) 102 | if (res.discovering) { 103 | this.onBluetoothDeviceFound() 104 | } else if (res.available) { 105 | this.startBluetoothDevicesDiscovery() 106 | } 107 | } 108 | }) 109 | }, 110 | startBluetoothDevicesDiscovery() { 111 | if (this._discoveryStarted) { 112 | return 113 | } 114 | this._discoveryStarted = true 115 | wx.startBluetoothDevicesDiscovery({ 116 | success: (res) => { 117 | console.log('startBluetoothDevicesDiscovery success', res) 118 | this.onBluetoothDeviceFound() 119 | }, 120 | fail: (res) => { 121 | console.log('startBluetoothDevicesDiscovery fail', res) 122 | } 123 | }) 124 | }, 125 | stopBluetoothDevicesDiscovery() { 126 | wx.stopBluetoothDevicesDiscovery({ 127 | complete: () => { 128 | console.log('stopBluetoothDevicesDiscovery') 129 | this._discoveryStarted = false 130 | } 131 | }) 132 | }, 133 | onBluetoothDeviceFound() { 134 | wx.onBluetoothDeviceFound((res) => { 135 | 136 | console.log('onBluetoothDeviceFound res',res) 137 | res.devices.forEach(device => { 138 | if (!device.name && !device.localName) { 139 | return 140 | } 141 | 142 | console.log('onBluetoothDeviceFound device',device) 143 | 144 | const foundDevices = this.data.devices 145 | const idx = inArray(foundDevices, 'deviceId', device.deviceId) 146 | const data = {} 147 | if (idx === -1) { 148 | data[`devices[${foundDevices.length}]`] = device 149 | } else { 150 | data[`devices[${idx}]`] = device 151 | } 152 | this.setData(data) 153 | }) 154 | }) 155 | }, 156 | createBLEConnection(e) { 157 | const ds = e.currentTarget.dataset 158 | const deviceId = ds.deviceId 159 | const name = ds.name 160 | this._createBLEConnection(deviceId, name) 161 | }, 162 | _createBLEConnection(deviceId, name) { 163 | wx.showLoading() 164 | wx.createBLEConnection({ 165 | deviceId, 166 | success: () => { 167 | console.log('createBLEConnection success'); 168 | this.setData({ 169 | connected: true, 170 | name, 171 | deviceId, 172 | }) 173 | this.getBLEDeviceServices(deviceId) 174 | wx.setStorage({ 175 | key: LAST_CONNECTED_DEVICE, 176 | data: name + ':' + deviceId 177 | }) 178 | }, 179 | complete() { 180 | wx.hideLoading() 181 | }, 182 | fail: (res) => { 183 | console.log('createBLEConnection fail', res) 184 | } 185 | }) 186 | this.stopBluetoothDevicesDiscovery() 187 | }, 188 | closeBLEConnection() { 189 | wx.closeBLEConnection({ 190 | deviceId: this.data.deviceId 191 | }) 192 | this.setData({ 193 | connected: false, 194 | chs: [], 195 | canWrite: false, 196 | barcode:[], 197 | }) 198 | }, 199 | getBLEDeviceServices(deviceId) { 200 | wx.getBLEDeviceServices({ 201 | deviceId, 202 | success: (res) => { 203 | console.log('getBLEDeviceServices', res) 204 | for (let i = 0; i < res.services.length; i++) { 205 | if (res.services[i].isPrimary) { 206 | this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid) 207 | } 208 | } 209 | } 210 | }) 211 | }, 212 | getBLEDeviceCharacteristics(deviceId, serviceId) { 213 | let that = this; 214 | wx.getBLEDeviceCharacteristics({ 215 | deviceId, 216 | serviceId, 217 | success: (res) => { 218 | console.log('getBLEDeviceCharacteristics success', res.characteristics) 219 | // 这里会存在特征值是支持write,写入成功但是没有任何反应的情况 220 | // 只能一个个去试 221 | for (let i = 0; i < res.characteristics.length; i++) { 222 | const item = res.characteristics[i] 223 | if (item.properties.indicate){ 224 | console.log('getBLEDeviceCharacteristics indicate item is true', item) 225 | }else{ 226 | console.log('getBLEDeviceCharacteristics indicate item is false', item) 227 | return 228 | } 229 | if ( item.properties.notify && item.properties.indicate) { 230 | this.setData({ 231 | canWrite: true 232 | }) 233 | this._deviceId = deviceId 234 | this._serviceId = serviceId 235 | this._characteristicId = item.uuid 236 | 237 | 238 | wx.notifyBLECharacteristicValueChange({ 239 | characteristicId: item.uuid, 240 | 241 | deviceId: deviceId, 242 | 243 | serviceId: serviceId, 244 | 245 | state: true, 246 | type:'notification', 247 | fail (res) { 248 | console.log({res}) 249 | }, 250 | complete:function(res){ 251 | console.log('44444444444444444444444',res) 252 | 253 | }, 254 | success:function(res){ 255 | 256 | console.log('notify启用',res); 257 | wx.onBLECharacteristicValueChange((result) => { 258 | console.log('监听特征值变化',result); 259 | const barcode = [ab2str(result.value),...that.data.barcode]; 260 | that.setData({barcode}) 261 | }) 262 | } 263 | }); 264 | break; 265 | } 266 | } 267 | }, 268 | fail(res) { 269 | console.error('getBLEDeviceCharacteristics', res) 270 | } 271 | }) 272 | }, 273 | writeBLECharacteristicValue() { 274 | let printerJobs = new PrinterJobs(); 275 | printerJobs 276 | .print('2018年12月5日17:34') 277 | .print(printerUtil.fillLine()) 278 | .setAlign('ct') 279 | .setSize(2, 2) 280 | .print('#20饿了么外卖') 281 | .setSize(1, 1) 282 | .print('切尔西Chelsea') 283 | .setSize(2, 2) 284 | .print('在线支付(已支付)') 285 | .setSize(1, 1) 286 | .print('订单号:5415221202244734') 287 | .print('下单时间:2017-07-07 18:08:08') 288 | .setAlign('lt') 289 | .print(printerUtil.fillAround('一号口袋')) 290 | .print(printerUtil.inline('意大利茄汁一面 * 1', '15')) 291 | .print(printerUtil.fillAround('其他')) 292 | .print('餐盒费:1') 293 | .print('[赠送康师傅冰红茶] * 1') 294 | .print(printerUtil.fillLine()) 295 | .setAlign('rt') 296 | .print('原价:¥16') 297 | .print('总价:¥16') 298 | .setAlign('lt') 299 | .print(printerUtil.fillLine()) 300 | .print('备注') 301 | .print("无") 302 | .print(printerUtil.fillLine()) 303 | .println(); 304 | 305 | let buffer = printerJobs.buffer(); 306 | console.log('ArrayBuffer', 'length: ' + buffer.byteLength, ' hex: ' + ab2hex(buffer)); 307 | // 1.并行调用多次会存在写失败的可能性 308 | // 2.建议每次写入不超过20字节 309 | // 分包处理,延时调用 310 | const maxChunk = 20; 311 | const delay = 20; 312 | for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) { 313 | let subPackage = buffer.slice(i, i + maxChunk <= length ? (i + maxChunk) : length); 314 | setTimeout(this._writeBLECharacteristicValue, j * delay, subPackage); 315 | } 316 | }, 317 | _writeBLECharacteristicValue(buffer) { 318 | wx.writeBLECharacteristicValue({ 319 | deviceId: this._deviceId, 320 | serviceId: this._serviceId, 321 | characteristicId: this._characteristicId, 322 | value: buffer, 323 | success(res) { 324 | console.log('writeBLECharacteristicValue success', res) 325 | }, 326 | fail(res) { 327 | console.log('writeBLECharacteristicValue fail', res) 328 | } 329 | }) 330 | }, 331 | closeBluetoothAdapter() { 332 | wx.closeBluetoothAdapter() 333 | this._discoveryStarted = false 334 | }, 335 | onLoad(options) { 336 | const lastDevice = wx.getStorageSync(LAST_CONNECTED_DEVICE); 337 | this.setData({ 338 | lastDevice: lastDevice 339 | }) 340 | }, 341 | createBLEConnectionWithDeviceId(e) { 342 | // 小程序在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备 343 | const device = this.data.lastDevice 344 | if (!device) { 345 | return 346 | } 347 | const index = device.indexOf(':'); 348 | const name = device.substring(0, index); 349 | const deviceId = device.substring(index + 1, device.length); 350 | console.log('createBLEConnectionWithDeviceId', name + ':' + deviceId) 351 | wx.openBluetoothAdapter({ 352 | success: (res) => { 353 | console.log('openBluetoothAdapter success', res) 354 | this._createBLEConnection(deviceId, name) 355 | }, 356 | fail: (res) => { 357 | console.log('openBluetoothAdapter fail', res) 358 | if (res.errCode === 10001) { 359 | wx.showModal({ 360 | title: '错误', 361 | content: '未找到蓝牙设备, 请打开蓝牙后重试。', 362 | showCancel: false 363 | }) 364 | wx.onBluetoothAdapterStateChange((res) => { 365 | console.log('onBluetoothAdapterStateChange', res) 366 | if (res.available) { 367 | // 取消监听 368 | wx.onBluetoothAdapterStateChange(() => { 369 | }); 370 | this._createBLEConnection(deviceId, name) 371 | } 372 | }) 373 | } 374 | } 375 | }) 376 | } 377 | }) -------------------------------------------------------------------------------- /miniprogram_npm/text-encoding/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["..\\..\\node_modules\\text-encoding\\index.js","..\\..\\node_modules\\text-encoding\\lib\\encoding.js","..\\..\\node_modules\\text-encoding\\lib\\encoding-indexes.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,AENA,ADGA;ADIA,AENA,ADGA;ADIA,AENA,ADGA;ADIA,AENA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js"} -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 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 | 61 | 62 | 63 | 64 | function 65 | => 66 | 0A 67 | 1B 68 | buffer 69 | PrinterJob 70 | COLOR 71 | 21 72 | FEED_CONTROL_SEQUENCES 73 | CS_DEFAULT 74 | TXT_FONT_ 75 | 25 76 | ff 77 | 24 78 | the code point 79 | utf 80 | gbk 81 | '\x 82 | ' 83 | \x 84 | ab2hex 85 | CHARACTER_SPACING 86 | getBLEDeviceServices 87 | onBLEConnectionStateChange 88 | openBluetoothAdapter 89 | onBluetoothAdapterStateChange 90 | _discoveryStarted 91 | startBluetoothDevicesDiscovery 92 | connected 93 | name 94 | 95 | 96 | [0x 97 | ] 98 | ,0x 99 | 100 | 101 | 102 | 104 | 105 | 123 | 124 | 125 | 126 | 127 | true 128 | DEFINITION_ORDER 129 | 130 | 131 | 132 | 133 | 134 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | General 148 | 149 | 150 | JavaScript 151 | 152 | 153 | Naming conventionsJavaScript 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 |