commit 04e2b4680a0ab76179ebcb34f807d40aa670a04e Author: langxiankui <33216671+langxiankui@users.noreply.github.com> Date: Tue Jul 20 14:36:44 2021 +0800 Add files via upload diff --git a/README.md b/README.md new file mode 100644 index 0000000..87bb8fd --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# 超高频(UHF) RFID 卡读写的 Cordova 插件 + +## 说明 +**使用UHF1_Com13_SDK_v1.5,针对k71v1_64_bsp型号设备** + +## 安装 +使用 Cordova +
cordova plugin add https://github.com/shuto-cn/UHF#Com13_SDK_v1.5
+ +使用 Ionic +
ionic cordova plugin add https://github.com/shuto-cn/UHF#Com13_SDK_v1.5
+ +## 示例程序 +https://github.com/shuto-cn/uhf-demo/tree/Com13_SDK_v1.5 + +## 目前提供的功能 +### 单次询卡 - 读取卡的 EPC +* 调用: +
cordova.plugins.uhf.searchCard(successCallBack, errorCallback);
+* 参数: +* 返回值: +EPC数组,因为可能读到多个。
["30396062C3AE88C00021E2BC"]
+* 多次巡卡方法已取消,如有需求请用js实现。 + +### 写卡 +* 调用: +
cordova.plugins.uhf.writeCard(message, successCallBack, errorCallback);
+* 参数: +
+{
+  data: "内容"
+}
+
+* 内容为需要写入user区的数据,支持中英文,上限为128位16进制 +* 返回值:“写入成功”或“写入失败” + +### 读卡 +* 仅读取user区的内容 +* 调用: +
cordova.plugins.uhf.readCard(successCallBack, errorCallback);
+* 返回值: +
"内容"
+ +### 设置功率 +* 调用: +
cordova.plugins.uhf.setPower(power, successCallBack, errorCallback);
+* 参数: +
+目前支持的功率值为 16 至 26,超出范围会按最大或最小值处理。
+
+* 返回值:“设置成功”或“设置失败” diff --git a/package.json b/package.json new file mode 100644 index 0000000..05960d8 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "cordova-plugin-uhf", + "version": "0.0.1", + "description": "", + "cordova": { + "id": "cordova-plugin-uhf", + "platforms": [ + "android" + ] + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android" + ], + "author": "", + "license": "ISC" +} diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..7ea287c --- /dev/null +++ b/plugin.xml @@ -0,0 +1,44 @@ + + + Cordova UHF Plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/SerialPort.jar b/src/android/SerialPort.jar new file mode 100644 index 0000000..3f1d11c Binary files /dev/null and b/src/android/SerialPort.jar differ diff --git a/src/android/cordova/plugin/uhf/UHF.java b/src/android/cordova/plugin/uhf/UHF.java new file mode 100644 index 0000000..3256ba8 --- /dev/null +++ b/src/android/cordova/plugin/uhf/UHF.java @@ -0,0 +1,256 @@ +package cordova.plugin.uhf; + +import android.util.Log; +import cn.pda.serialport.Tools; +import com.android.hdhe.uhf.reader.UhfReader; +import com.android.hdhe.uhf.readerInterface.TagModel; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.List; +import java.util.Objects; + +/** + * 超高频(UHF)读写卡插件。 + * 因不同的机型使用的so库不同,故无法适配所有机型。 + */ +public class UHF extends CordovaPlugin { + private UhfReader manager; + private String selectedEpc = ""; + private byte[] password = Tools.HexString2Bytes("00000000"); + private boolean threadFlag; + + @Override + protected void pluginInitialize() { + super.pluginInitialize(); + manager = UhfReader.getInstance(); + } + + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + switch (action) { + case "readCard": + this.readCard(callbackContext); + return true; + case "searchCard": + this.searchCard(callbackContext); + return true; + case "writeCard": + this.writeCard(args, callbackContext); + return true; + case "setPower": + this.setPower(args, callbackContext); + return true; + case "startWork": + this.startWork(callbackContext); + return true; + case "endWork": + this.endWork(callbackContext); + return true; + case "selectCard": + this.selectCard(args, callbackContext); + return true; + case "inventoryCard": + this.inventoryCard(callbackContext); + return true; + case "stopInventoryCard": + this.stopInventoryCard(callbackContext); + return true; + } + return false; + } + + private void startWork(CallbackContext callbackContext) { + if (this.manager == null) { + manager = UhfReader.getInstance(); + } + callbackContext.success(); + } + + private void endWork(CallbackContext callbackContext) { + if (this.manager != null) { + this.manager.close(); + this.manager = null; + } + callbackContext.success(); + } + + + private void stopInventoryCard(CallbackContext callbackContext) { + threadFlag = false; + callbackContext.success("停止"); + } + + private void searchCard(CallbackContext callbackContext) { + try { + JSONArray ja = onceSearchCard(); + callbackContext.success(ja); + } catch (JSONException e) { + callbackContext.error(e.getMessage()); + } + } + + private void inventoryCard(CallbackContext callbackContext) { + threadFlag = true; + Thread thread = new InventoryThread(callbackContext); + thread.start(); + } + + private class InventoryThread extends Thread { + + private CallbackContext cb; + + public InventoryThread(CallbackContext cb) { + super(); + this.cb = cb; + } + + public InventoryThread() { + super(); + } + + @Override + public void run() { + super.run(); + try { + while (threadFlag) { + JSONArray ja = onceSearchCard(); + PluginResult pr = new PluginResult(PluginResult.Status.OK, ja); + pr.setKeepCallback(true); + cb.sendPluginResult(pr); + Thread.sleep(100); + } + } catch (Exception e) { + cb.error(e.getMessage()); + } + } + } + + + /** + * 单次巡卡,每执行一次,就扫描一次范围内的卡片,将巡到的卡片信息以json数组的形式返回。 + */ + private JSONArray onceSearchCard() throws JSONException { + List tagList; + tagList = manager.inventoryRealTime(); //实时盘存 + JSONArray ja = new JSONArray(); + JSONObject jo; + String epcStr; + if (tagList != null && !tagList.isEmpty()) { + for (TagModel tag : tagList) { + if (tag == null) { + break; + } else { + epcStr = Tools.Bytes2HexString(tag.getmEpcBytes(), tag.getmEpcBytes().length); + } + jo = new JSONObject(); + jo.put("mEpcBytes", epcStr); + jo.put("mRssi", tag.getmRssi()); + ja.put(jo); + } + } + return ja; + } + + /** + * 读卡,将USER区的hex字符串转换为ascii读取,因EPC区与TID区不能写入,故暂时只读取USER区,注释掉的部分为适应EPC区与TID区读取的代码 + * 将注释解开后应抛出JSONException + */ + private void readCard(CallbackContext callbackContext) { +// JSONObject obj = message.getJSONObject(0); +// int site = obj.getInt("site"); +// int addr = obj.getInt("addr"); +// if (site == 1) { +// addr = 2; +// length = 6; +// } else if (site == 3) { +// addr = 0; +// length = 32; +// } else if (site == 2) { +// addr = 0; +// length = 12; +// } + manager.selectEPC(Tools.HexString2Bytes(this.selectedEpc)); + int length = 32; + byte[] data = manager.readFrom6C(3, 0, length, password); + if (data != null && data.length >= 1) { + String msg; +// if (site == 3) { // 读取User区的时候,将16进制字符串转换为ascii + msg = Util.bytes2Str(data); +// } else { // 因EPC区与TID区不能写入,故读取时不转码 +// msg = Tools.Bytes2HexString(data, data.length); +// } + callbackContext.success(msg); + } else { + callbackContext.error("读取失败"); + } + } + + /** + * 选卡,将选到的卡储存在this.selectedEpc中 + */ + private void selectCard(JSONArray message, CallbackContext callbackContext) throws JSONException { + JSONObject obj = message.getJSONObject(0); + String epc = obj.getString("epc"); + manager.selectEPC(Tools.HexString2Bytes(epc)); + this.selectedEpc = epc; + callbackContext.success(); + } + + /** + * 写卡,将ascii转换为bytes并写入 + */ + private void writeCard(JSONArray message, CallbackContext callbackContext) { + manager.selectEPC(Tools.HexString2Bytes(this.selectedEpc)); + String _data = null; + try { + JSONObject obj = message.getJSONObject(0); + _data = Util.str2HexStr(obj.getString("data")); + } catch (JSONException e) { + callbackContext.error("JSON解析失败"); + } + // 处理_data,将ascii转换为16进制字符串,超过32 * 4位的16进制字符串报错,小于32 * 4时在16进制字符串末位补0x00作为完结标记,再转换为bytes + if (_data != null && _data.length() > 32 * 4) { + callbackContext.error("数据过长"); + return; + } + if (_data.length() < 32 * 4) { + _data += "00"; + } + byte[] dataBytes = Util.hexStringToBytes(_data); + boolean writeFlag = false; + if (dataBytes != null) { + writeFlag = manager.writeTo6C(password, 3, 0, dataBytes.length / 2, dataBytes); + } + if (writeFlag) { + callbackContext.success("写入成功"); + } else { + callbackContext.error("写入失败"); + } + } + + private void setPower(JSONArray message, CallbackContext callbackContext) throws JSONException { + int power = message.getInt(0); + if (power > 26) { + power = 26; + } else if (power < 16) { + power = 16; + } + if (manager.setOutputPower(power)) { + callbackContext.success("设置成功"); + } else { + callbackContext.error("设置失败"); + } + } + + @Override + public void onDestroy() { + if (this.manager != null) { + this.manager.close(); + } + } +} diff --git a/src/android/cordova/plugin/uhf/Util.java b/src/android/cordova/plugin/uhf/Util.java new file mode 100644 index 0000000..ee1de93 --- /dev/null +++ b/src/android/cordova/plugin/uhf/Util.java @@ -0,0 +1,77 @@ +package cordova.plugin.uhf; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import android.content.Context; +import android.media.AudioManager; +import android.media.SoundPool; +import android.util.Log; + +/** + * 工具类,主要用于实现各类字符串之间的转换 + */ +public class Util { + + public static String str2HexStr(String origin) { + byte[] bytes = origin.getBytes(); + String hex = bytesToHexString(bytes, bytes.length); + return hex; + } + + public static String hexStr2Str(String hex) { + byte[] bb = hexStringToBytes(hex); + String rr = new String(bb); + return rr; + } + + public static String bytes2Str(byte[] src) { + String rr = bytesToHexString(src, src.length); + String str = new String(hexStr2Str(rr)); + return str; + } + + private static String bytesToHexString(byte[] src, int size) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || size <= 0) { + return null; + } + for (int i = 0; i < size; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + String str = stringBuilder.toString(); + if (str.length() < 128) { + str += "00"; + } + return str; + } + + public static byte[] hexStringToBytes(String hexString) { + if (hexString == null || hexString.equals("")) { + return null; + } + hexString = hexString.toUpperCase(); + int length = hexString.length() / 2; + char[] hexChars = hexString.toCharArray(); + byte[] d = new byte[length]; + for (int i = 0; i < length; i++) { + int pos = i * 2; + // 以0x00为分界线,不读取后面的数据 + if ((byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])) == 00) { + break; + } + d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); + } + return d; + } + + private static byte charToByte(char c) { + return (byte) "0123456789ABCDEF".indexOf(c); + } +} diff --git a/src/android/libs/arm64-v8a/libdevapi.so b/src/android/libs/arm64-v8a/libdevapi.so new file mode 100644 index 0000000..cd88f3f Binary files /dev/null and b/src/android/libs/arm64-v8a/libdevapi.so differ diff --git a/src/android/libs/arm64-v8a/libirdaSerialPort.so b/src/android/libs/arm64-v8a/libirdaSerialPort.so new file mode 100644 index 0000000..8c43d80 Binary files /dev/null and b/src/android/libs/arm64-v8a/libirdaSerialPort.so differ diff --git a/src/android/libs/arm64-v8a/libuhf.so b/src/android/libs/arm64-v8a/libuhf.so new file mode 100644 index 0000000..9907a5d Binary files /dev/null and b/src/android/libs/arm64-v8a/libuhf.so differ diff --git a/src/android/libs/armeabi-v7a/libdevapi.so b/src/android/libs/armeabi-v7a/libdevapi.so new file mode 100644 index 0000000..5b7e0c7 Binary files /dev/null and b/src/android/libs/armeabi-v7a/libdevapi.so differ diff --git a/src/android/libs/armeabi-v7a/libirdaSerialPort.so b/src/android/libs/armeabi-v7a/libirdaSerialPort.so new file mode 100644 index 0000000..b8a8974 Binary files /dev/null and b/src/android/libs/armeabi-v7a/libirdaSerialPort.so differ diff --git a/src/android/libs/armeabi-v7a/libuhf.so b/src/android/libs/armeabi-v7a/libuhf.so new file mode 100644 index 0000000..1c1874a Binary files /dev/null and b/src/android/libs/armeabi-v7a/libuhf.so differ diff --git a/src/android/libs/armeabi/libdevapi.so b/src/android/libs/armeabi/libdevapi.so new file mode 100644 index 0000000..5b7e0c7 Binary files /dev/null and b/src/android/libs/armeabi/libdevapi.so differ diff --git a/src/android/libs/armeabi/libirdaSerialPort.so b/src/android/libs/armeabi/libirdaSerialPort.so new file mode 100644 index 0000000..b8a8974 Binary files /dev/null and b/src/android/libs/armeabi/libirdaSerialPort.so differ diff --git a/src/android/libs/armeabi/libuhf.so b/src/android/libs/armeabi/libuhf.so new file mode 100644 index 0000000..1c1874a Binary files /dev/null and b/src/android/libs/armeabi/libuhf.so differ diff --git a/src/android/uhfcom13_v15.jar b/src/android/uhfcom13_v15.jar new file mode 100644 index 0000000..9994591 Binary files /dev/null and b/src/android/uhfcom13_v15.jar differ diff --git a/www/cordova-plugin-UHF.js b/www/cordova-plugin-UHF.js new file mode 100644 index 0000000..704cb08 --- /dev/null +++ b/www/cordova-plugin-UHF.js @@ -0,0 +1,41 @@ +var exec = require('cordova/exec'); + +var coolMethod = function () {}; + +coolMethod.readCard = function (success, error) { + exec(success, error, 'UHF', 'readCard', []); +} + +coolMethod.inventoryCard = function (success, error) { + exec(success, error, 'UHF', 'inventoryCard', []); +} + +coolMethod.stopInventoryCard = function (success, error) { + exec(success, error, 'UHF', 'stopInventoryCard', []); +} + +coolMethod.searchCard = function (success, error) { + exec(success, error, 'UHF', 'searchCard', []); +} + +coolMethod.writeCard = function (arg, success, error) { + exec(success, error, 'UHF', 'writeCard', [arg]); +} + +coolMethod.setPower = function (arg, success, error) { + exec(success, error, 'UHF', 'setPower', [arg]); +} + +coolMethod.startWork = function (success, error) { + exec(success, error, 'UHF', 'startWork', []); +} + +coolMethod.endWork = function (success, error) { + exec(success, error, 'UHF', 'endWork', []); +} + +coolMethod.selectCard = function (arg, success, error) { + exec(success, error, 'UHF', 'selectCard', [arg]); +} + +module.exports = coolMethod;