nfcTool.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. import {
  2. formatNullCharacter,
  3. stringToArrayBuffer,
  4. arrayBufferToString,
  5. ab2hex,
  6. byteLength,
  7. generateUUID
  8. } from './util.js'
  9. // 标签写入的类型
  10. export const WRITE_TYPE_ENUM = {
  11. T: 'T',
  12. U: 'U'
  13. };
  14. export const WRITE_TYPE_TEXT_ENUM = {
  15. T: '文本',
  16. U: '网址',
  17. };
  18. export const NFC_STATUS_ENUM = {
  19. INIT: 'INIT', // 初始化NFC
  20. INIT_OK: 'INIT_OK', // 初始化NFC成功
  21. INIT_ERROR: 'INIT_ERROR', // 初始化NFC失败
  22. READ_START: 'READ_START', // 读取开始
  23. READING: 'READING', // 正在读取
  24. READ_SUCCESS: 'READ_SUCCESS', // 读取成功
  25. READ_FAIL: 'READ_FAIL', // 读取失败
  26. WRITE_START: 'WRITE_START', // 写入开始
  27. WRITING: 'WRITING', // 正在写入
  28. WRITE_SUCCESS: 'WRITE_SUCCESS', // 写入成功
  29. WRITE_FAIL: 'WRITE_FAIL', // 写入失败
  30. DEVICE_NO_NFC: 'DEVICE_NO_NFC', // 设备不支持NFC
  31. DEVICE_NFC_NOT_ENABLE: 'DEVICE_NFC_NOT_ENABLE', // 设备未开启NFC
  32. TIMEOUT_ERROR: 'TIMEOUT_ERROR', // 超时提示
  33. TOAST: 'TOAST', // toast提示
  34. };
  35. const ERR_CODE_ENUM = {
  36. 13000: '本设备不支持NFC',
  37. 13001: '系统NFC开关未打开',
  38. 13010: '未知错误',
  39. 13019: '用户未授权',
  40. 13021: '已经开始NFC扫描',
  41. 13018: '尝试在未开始NFC扫描时停止NFC扫描',
  42. 13022: '标签已经连接',
  43. 13023: '尝试在未连接标签时断开连接',
  44. 13013: '未扫描到NFC标签',
  45. 13014: '无效的标签技术',
  46. 13015: '从标签上获取对应技术失败',
  47. 13024: '当前标签技术不支持该功能',
  48. 13017: '相关读写操作失败',
  49. 13016: '连接失败',
  50. }
  51. class NFCTool {
  52. static NFCAdapter = null; // NFC适配器对象
  53. static NFCType = null; // NFC标签
  54. nfcState = false; // nfc开启状态
  55. isConnect = false; // 开启连接
  56. noNFC = false; // 是否有NFC功能
  57. callBack = null;
  58. readFuc = null;
  59. writeFuc = null;
  60. timeOut = 60 * 1000;
  61. maxLength = 120;
  62. // 写入NFC的相关数据
  63. readyWrite = false; // 开启写
  64. writeTimer = null;
  65. writeParams = {
  66. id: '', // 将要写入ID
  67. text: '', // 写入的字符
  68. type: '', // 写入的类型
  69. };
  70. nfcTagId = ''; // nfc唯一标识
  71. // 读取NFC的相关数据
  72. readyRead = false; // 开启读
  73. readTimer = null;
  74. readParams = {
  75. id: '', // 读取ID
  76. text: '', // 读取的字符
  77. type: '', // 读取的类型
  78. };
  79. constructor(NFCToolCallBack, data) {
  80. this.callBack = NFCToolCallBack;
  81. this.timeOut = data?.timeOutNum ? Number(data?.timeOutNum) : 60 * 1000;
  82. // this.startDiscovery();
  83. }
  84. /**
  85. * 开始监听
  86. */
  87. startDiscovery(data) {
  88. console.log(data, uni.getSystemInfoSync().platform)
  89. if (uni.getSystemInfoSync().platform !== "android") {
  90. return
  91. }
  92. let _this = this;
  93. _this.readyWrite = data?.readyWrite ? data?.readyWrite : false;
  94. _this.callBack({
  95. status: NFC_STATUS_ENUM.INIT,
  96. tip: '初始化NFC'
  97. })
  98. // try {
  99. if (!_this.nfcState) {
  100. let adapter = wx.getNFCAdapter();
  101. console.log(adapter)
  102. if (adapter == null) {
  103. _this.callBack({
  104. status: NFC_STATUS_ENUM.DEVICE_NO_NFC,
  105. tip: '设备不支持NFC或未开启NFC'
  106. })
  107. _this.noNFC = true;
  108. return;
  109. }
  110. _this.NFCAdapter = adapter;
  111. _this.nfcState = true;
  112. _this.NFCAdapter.startDiscovery({
  113. success(res) {
  114. _this.callBack({
  115. status: NFC_STATUS_ENUM.INIT_OK,
  116. tip: 'NFC读取功能已开启',
  117. });
  118. // 绑定监听 NFC Tag 读取
  119. _this.readFuc = _this.readDiscoverHandler.bind(_this);
  120. _this.NFCAdapter.onDiscovered(_this.readFuc);
  121. if (_this.readyWrite) {
  122. // 绑定监听 NFC Tag 写入
  123. _this.writeTimer = setTimeout(() => {
  124. if (_this.readyWrite && !_this.isConnect) {
  125. _this.callBack({
  126. status: NFC_STATUS_ENUM.TIMEOUT_ERROR,
  127. tip: 'NFC读取超时,请重试...'
  128. })
  129. _this.readyRead = false;
  130. _this.stopDiscovery();
  131. }
  132. clearTimeout(_this.writeTimer);
  133. }, _this.timeOut);
  134. _this.writeFuc = _this.writeDiscoverHandler.bind(_this);
  135. _this.NFCAdapter.onDiscovered(_this.writeFuc);
  136. }
  137. // }
  138. },
  139. fail(err) {
  140. let errCodeTips = err?.errCode ? ERR_CODE_ENUM[Number(err?.errCode)] : '';
  141. _this.callBack({
  142. status: NFC_STATUS_ENUM.INIT_ERROR,
  143. tip: '请检查NFC功能是否正常',
  144. data: errCodeTips
  145. });
  146. _this.noNFC = true;
  147. }
  148. })
  149. }
  150. // } catch (e) {
  151. // _this.callBack({
  152. // status: NFC_STATUS_ENUM.INIT_ERROR,
  153. // tip: '请检查NFC功能是否正常'
  154. // })
  155. // _this.noNFC = true;
  156. // }
  157. }
  158. /**
  159. * 读取NFC标签
  160. */
  161. readData() {
  162. let _this = this;
  163. if (_this.nfcState) {
  164. if (_this.noNFC) {
  165. _this.callBack({
  166. status: NFC_STATUS_ENUM.READ_FAIL,
  167. tip: '请检查设备是否支持并开启 NFC 功能!',
  168. data: ''
  169. })
  170. return;
  171. }
  172. _this.readTimer = setTimeout(() => {
  173. if (_this.readyRead) {
  174. _this.callBack({
  175. status: NFC_STATUS_ENUM.TIMEOUT_ERROR,
  176. tip: 'NFC读取超时,请重试...'
  177. })
  178. _this.readyRead = false;
  179. }
  180. clearTimeout(_this.readTimer);
  181. }, _this.timeOut);
  182. _this.callBack({
  183. status: NFC_STATUS_ENUM.READ_START,
  184. tip: '请将设备靠近NFC识别区'
  185. })
  186. // 更改读状态
  187. _this.readyRead = true;
  188. }
  189. }
  190. /**
  191. * 监听 NFC Tag 读取
  192. * @param id ArrayBuffer
  193. * @param techs Array tech 数组,用于匹配NFC卡片具体可以使用什么标准(NfcA等实例)处理
  194. * @param messages
  195. */
  196. async readDiscoverHandler({
  197. id,
  198. techs,
  199. messages
  200. }) {
  201. let _this = this;
  202. try {
  203. if (_this.readyRead && _this.nfcState) {
  204. _this.callBack({
  205. status: NFC_STATUS_ENUM.READING,
  206. tip: '识别NFC标签中'
  207. })
  208. let adapter = _this.NFCAdapter;
  209. await _this.readTag(id);
  210. if (techs.includes(adapter.tech.nfcA)) {
  211. let nfcA = adapter.getNfcA();
  212. _this.NFCType = nfcA;
  213. if (messages) {
  214. let cordsArray = messages[0].records;
  215. cordsArray.forEach(item => {
  216. _this.readParams = {
  217. id: arrayBufferToString(item.id), // 读取ID
  218. text: arrayBufferToString(item.payload), // 读取的字符
  219. type: arrayBufferToString(item.type), // 读取的类型
  220. }
  221. _this.callBack({
  222. status: NFC_STATUS_ENUM.READ_SUCCESS,
  223. tip: '读取成功',
  224. data: {
  225. ..._this.readParams,
  226. nfcTagId: _this.nfcTagId,
  227. }
  228. })
  229. })
  230. } else {
  231. _this.callBack({
  232. status: NFC_STATUS_ENUM.READ_SUCCESS,
  233. tip: '读取成功',
  234. data: {
  235. ..._this.readParams,
  236. nfcTagId: _this.nfcTagId,
  237. }
  238. })
  239. }
  240. _this.readyRead = false;
  241. }
  242. }
  243. } catch (e) {
  244. _this.callBack({
  245. status: NFC_STATUS_ENUM.READ_FAIL,
  246. tip: '读取失败'
  247. })
  248. }
  249. }
  250. /**
  251. * 写入NFC标签
  252. * @param {Object} data 写入的数据
  253. */
  254. writeTag(data) {
  255. let _this = this;
  256. if (_this.nfcState) {
  257. if (data) {
  258. _this.callBack({
  259. status: NFC_STATUS_ENUM.WRITE_START,
  260. tip: '请将设备靠近NFC识别区'
  261. })
  262. _this.writeParams = {
  263. id: data.id,
  264. text: data.positionName, // 写入的字符
  265. type: WRITE_TYPE_ENUM.T, // 写入的类型
  266. };
  267. if (byteLength(JSON.stringify(_this.writeParams)) > _this.maxLength) {
  268. _this.callBack({
  269. status: NFC_STATUS_ENUM.WRITE_FAIL,
  270. tip: '数据超过NFC容量大小,请重试'
  271. });
  272. _this.stopDiscovery();
  273. return;
  274. }
  275. if (_this.isConnect) {
  276. _this.writeNdefMessage()
  277. } else {
  278. // TODO 报错 NFC断开连接
  279. }
  280. } else {
  281. _this.callBack({
  282. status: NFC_STATUS_ENUM.WRITE_FAIL,
  283. tip: '写入数据不可为空,请重试'
  284. })
  285. }
  286. }
  287. }
  288. /**
  289. * 监听 NFC Tag 写入
  290. * @param id ArrayBuffer
  291. * @param techs Array tech 数组,用于匹配NFC卡片具体可以使用什么标准(NfcA等实例)处理
  292. * @param messages
  293. */
  294. async writeDiscoverHandler({
  295. id,
  296. techs,
  297. messages
  298. }) {
  299. let _this = this;
  300. if (_this.nfcState) {
  301. _this.callBack({
  302. status: NFC_STATUS_ENUM.READING,
  303. tip: '识别NFC标签中'
  304. })
  305. let adapter = _this.NFCAdapter;
  306. await _this.readTag(id);
  307. let nDef = adapter.getNdef();
  308. _this.NFCType = nDef;
  309. if (_this.isConnect) {
  310. _this.onCloseConnect();
  311. }
  312. _this.onConnect();
  313. }
  314. }
  315. /**
  316. * 读取 NFC 标签 返回的 id
  317. * @param {Object} id
  318. */
  319. readTag(id) {
  320. let aid = parseInt(ab2hex(id), 16);
  321. // 使用 new Uint8Array处理返回一个数组
  322. const data = new Uint8Array(id);
  323. let tagId = ""
  324. data.forEach(e => {
  325. // 因为我们的卡号是16位制的,所以这里选择转换成16位数字
  326. let item = e.toString(16)
  327. if (item.length == 1) {
  328. item = '0' + item
  329. }
  330. // 因为我们需要的是大写,所以要转
  331. item = item.toUpperCase()
  332. tagId = item + tagId
  333. })
  334. // 最终结果
  335. /* 这里获取的tagId就是我们读取出的UID码了 */
  336. this.nfcTagId = tagId;
  337. }
  338. /**
  339. * 设备连接操作 写入操作
  340. */
  341. onConnect() {
  342. let _this = this;
  343. if (!_this.isConnect) {
  344. _this.NFCType.connect({
  345. success: function(cRes) {
  346. _this.isConnect = true;
  347. _this.callBack({
  348. status: NFC_STATUS_ENUM.WRITING,
  349. tip: '已连接,请勿移开设备',
  350. data: {
  351. isConnect: true,
  352. }
  353. })
  354. // _this.writeNdefMessage()
  355. },
  356. fail: function(err) {
  357. let errCodeTips = err?.errCode ? ERR_CODE_ENUM[Number(err?.errCode)] : '';
  358. _this.callBack({
  359. status: NFC_STATUS_ENUM.WRITE_FAIL,
  360. tip: '设备连接错误',
  361. data: {
  362. nfcTagId: _this.nfcTagId,
  363. errCodeTips
  364. }
  365. });
  366. _this.stopDiscovery();
  367. }
  368. });
  369. }
  370. }
  371. /**
  372. * NFC写入操作
  373. */
  374. writeNdefMessage() {
  375. let _this = this;
  376. // let idBuffer = stringToArrayBuffer(''); // 写入的字符
  377. let idBuffer = stringToArrayBuffer((_this.writeParams.id).toString()); // 写入的字符
  378. let textBuffer = stringToArrayBuffer(_this.writeParams.text); // 写入的字符
  379. let typeBuffer = stringToArrayBuffer(_this.writeParams.type); // 写入的类型
  380. const records = [{
  381. id: idBuffer,
  382. payload: textBuffer,
  383. type: typeBuffer,
  384. tnf: 2
  385. }];
  386. _this.NFCType.writeNdefMessage({
  387. records: records,
  388. success: function(tRes) {
  389. _this.callBack({
  390. status: NFC_STATUS_ENUM.WRITE_SUCCESS,
  391. tip: '写入成功',
  392. data: {
  393. ..._this.writeParams,
  394. nfcTagId: _this.nfcTagId,
  395. isConnect: false
  396. }
  397. })
  398. },
  399. fail: function(err) {
  400. let errCodeTips = err?.errCode ? ERR_CODE_ENUM[Number(err?.errCode)] : '';
  401. _this.callBack({
  402. status: NFC_STATUS_ENUM.WRITE_FAIL,
  403. tip: '数据写入失败',
  404. data: {
  405. nfcTagId: _this.nfcTagId,
  406. errCodeTips,
  407. isConnect: false
  408. }
  409. })
  410. },
  411. complete: res => {
  412. _this.writeParams = {
  413. id: '',
  414. text: '', // 写入的字符
  415. type: '', // 写入的类型
  416. };
  417. _this.readyWrite = false;
  418. _this.onCloseConnect();
  419. _this.stopDiscovery();
  420. }
  421. })
  422. }
  423. onCloseConnect() {
  424. let _this = this;
  425. _this.isConnect = false;
  426. if (_this.NFCType) {
  427. _this.NFCType.close({
  428. success: function(cRes) {},
  429. fail: function(err) {
  430. let errCodeTips = err?.errCode ? ERR_CODE_ENUM[Number(err?.errCode)] : '';
  431. },
  432. complete: res => {}
  433. });
  434. }
  435. }
  436. /**
  437. * 设置超时时间
  438. */
  439. onSetTimeout() {
  440. }
  441. /**
  442. * @param {Object} data格式化NFC数据
  443. */
  444. formatNFC(data) {
  445. // 这里可以添加数据格式化的逻辑
  446. return 'Formatted data: ' + data;
  447. }
  448. changeNFCState() {
  449. let _this = this;
  450. _this.nfcState = !_this.nfcState;
  451. }
  452. /**
  453. * 停止监听贴卡
  454. */
  455. stopDiscovery() {
  456. let _this = this;
  457. if (_this.NFCType) {
  458. _this.isConnect && _this.onCloseConnect();
  459. _this.NFCType = null;
  460. }
  461. if (_this.NFCAdapter) {
  462. _this.NFCAdapter.offDiscovered(_this.readFuc);
  463. _this.NFCAdapter.offDiscovered(_this.writeFuc);
  464. _this.NFCAdapter.stopDiscovery();
  465. _this.NFCAdapter = null;
  466. _this.nfcState = false;
  467. }
  468. if (_this.readTimer) {
  469. clearTimeout(_this.readTimer);
  470. }
  471. if (_this.writeTimer) {
  472. clearTimeout(_this.writeTimer);
  473. }
  474. }
  475. }
  476. export default NFCTool;