【湖南步联科技身份证】 身份证读取与酒店收银系统源码整合———未来之窗行业应用跨平台架构

时间:2024-09-29 21:08:30

 一、html5

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <script type="text/javascript" src="http://51.onelink.ynwlzc.net/o2o/tpl/Merchant/static/js/jquery.min.js"></script>
       
        <style type="text/css">
            html {
                height: 100%;
                width: 100%;
            }
            #readCard {
                width: 90px;
                margin: 10px;
            }
            #output {
                width: 500px;
                height: 200px;
            }
        </style>
    </head>
    <body>
       <h1>读身份证示例-湖南步联科技身份证
</h1> <h1>未来之窗星链-推进器测试
</h1><br>
        
        <input type="button" id="getReader" value="列出设备" style="margin:10px" onclick="getReader();" />
        <select id="readerList" style="width:250px;margin:10px"></select>
        <!-- 读卡参数<input type="text" id="readParam" value='{"portType": 5}' size="25" style="margin:10px" > -->


        <br/>
        <input type="button" id="readCard" value="读卡" onclick="startReadCard();" />
        <input type="checkbox" id="autoReadCard" value="autoRead"  >自动读卡
        <br>
        阅读次数:<input type="text" id="totalReadCount" name="Num" value="100" size="7">
        成功次数<input type="text" id="SuccessTimes" value="0" size="7">
        失败次数<input type="text" id="FailTimes" value="0" size="7">
        所用时间<input type="text" id="TimeExpand" value="0" size="7">
        成功率<input type="text" id="Rate" value="0%" size="5">
        平均耗时<input type="text" id="AvgTime" value="0" size="5">

        <br>
            开始时间<input type="text" id="StartTime" value="" size="25">
            完成时间<input type="text" id="EndTime" value="" size="25">
            单次用时<input type="text" id="ReadTime" value="111" size="25">
            
        


        <table border="1" cellpadding="0" cellspacing="2" bordercolor="#3333FF">
            <tr>
                <td><div align="right">姓名:</div></td>
                <td><input name="text_name" type="text" id="text_name" size="20"/></td>
                <td><div align="right">性别代码:</div></td>
                <td><input name="text_genderid" type="text" id="text_genderid" size="5"/></td>
                <td><div align="right">民族代码:</div></td>
                <td><input name="text_nationid" type="text" id="text_nationid" size="5"/></td>
                <td><div align="right">出生日期:</div></td>
                <td><input name="text_birthdate" type="text" id="text_birthdate" size="20"/></td>
            </tr>
            <tr>
                <td><div align="right">身份证号码:</div></td>
                <td><input name="text_idnumber" type="text" id="text_idnumber" size="20"/></td>
                <td><div align="right">签发机关:</div></td>
                <td><input name="text_signorgan" type="text" id="text_signorgan" size="20"/></td>
                <td><div align="right">起始有效期:</div></td>
                <td><input name="text_beginterm" type="text" id="text_beginterm" size="20"/></td>
                <td><div align="right">终止有效期:</div></td>
                <td><input name="text_endterm" type="text" id="text_endterm" size="20"/></td>
            </tr>
            <tr>
                <td><div align="right">住址:</div></td>
                <td colspan="5"><input name="text_address" type="text" id="text_address" size="82"/>
                </td>
                <td><div align="right">民族:</div></td>
                <td><input name="text_nationid" type="text" id="text_nation" size="5"/></td>

            </tr>
            <tr>
                <td height="43">
                    <div align="right">照片编码:</div>
                </td>
                <td colspan="7"><textarea name="text_photobase64" cols="80" rows="5" id="text_photobase64"></textarea></td>
                <td><img name="PhotoDisplay" id="PhotoDisplay" style="width:102px; height:126px;"/></td>
            </tr>
        </table>
        <br/>
		<textarea id="output"></textarea><br/>
    </body>
</html>


<script type="text/javascript" >

var baseUrl = "http://127.0.0.1:6045"
$(document).ready(function(){
     //解决IE浏览器不支持console,报错未定义问题
     window.console = window.console || (function () {
        var c ={};
        c.log = c.warn = c.debug = c.info = c.error = c.time = c.dir = c.profile= c.clear = c.exception = c.trace = c.assert = function(){};
        return c;
    })();
//    alert(location.host)
//    if(location.host){
//        baseUrl = location.protocol + "//" + location.host;
//    }
    console.log("baseurl "+ baseUrl);
    
});

function fixInt(val){
    if(val<10)
      return "0"+val;
     else
      return val;
}


function fmtDate(date){
    return date.getFullYear()+"-"+fixInt(date.getMonth()+1)+"-"+fixInt(date.getDate())+" "+fixInt(date.getHours())+":"+fixInt(date.getMinutes())+":"+fixInt(date.getSeconds())+":"+fixInt(date.getMilliseconds());
}

function clearForm() {
    $("#output").text("");
    $("#text_name").val("");
    $("#text_genderid").val("");
    $("#text_nationid").val("");
    $("#text_birthdate").val("");
    $("#text_address").val("");
    $("#text_idnumber").val("");
    $("#text_signorgan").val("");
    $("#text_beginterm").val("");
    $("#text_endterm").val("");
    
}

function getReader(){


    $.ajax({
		type: "get",
		dataType: "json", 
        timeout:10000,
        crossDomain: true,

        url: baseUrl + "/getCardReaderList",
        success: function(resultInfo) {
            $("#output").text(JSON.stringify(resultInfo));
            var list = document.getElementById("readerList");
            for(var i = list.options.length-1; i >= 0; i--){
                list.remove(i);
            }
            var strArray = resultInfo;
            for(var i = 0; i < resultInfo.length; i++){
                var newOption = document.createElement("option");
                newOption.setAttribute("version", strArray[i].version);
                newOption.setAttribute("index", strArray[i].index);
                newOption.appendChild(document.createTextNode(strArray[i].version));
                list.appendChild(newOption);
            }
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.error("getReader request error  ", textStatus, errorThrown );
            $("#output").text("getReader error status "+ textStatus+" errorThrown "  +errorThrown)
        }
    });
}
function parseCardWzxx(wzxx) {
  var result = {};

  result['xm'] = wzxx.substr(0, 15);
  result['xbdm'] = wzxx.substr(15, 1);
  result['mzdm'] = wzxx.substr(16, 2);
  result['csrq'] = wzxx.substr(18, 8);
  result['dzmc'] = wzxx.substr(26, 35);
  result['gmsfhm'] = wzxx.substr(61, 18);
  result['qfjgmc'] = wzxx.substr(79, 15);
  result['qfjgmc'] = wzxx.substr(79, 15);
  result['yxqqsrq'] = wzxx.substr(94, 8);
  result['yxqjzrq'] = wzxx.substr(102, 8);
  result['zjlxbs'] = wzxx.substr(124, 1);

  switch (result['xbdm']) {
    case '1': result['xbmc'] = '男'; break;
    case '2': result['xbmc'] = '女'; break;
    default: result['xbmc'] = '未知';
  }

  if (result['zjlxbs'] == 'J'){
    result['txzhm'] = wzxx.substr(110, 9);
    result['qfcs'] = wzxx.substr(119, 2);
  }

  switch (result['mzdm']) {
    case '01': result['mzmc'] = '汉'; break;
    case '02': result['mzmc'] = '蒙古'; break;
    case '03': result['mzmc'] = '回'; break;
    case '04': result['mzmc'] = '藏'; break;
    case '05': result['mzmc'] = '*'; break;
    case '06': result['mzmc'] = '苗'; break;
    case '07': result['mzmc'] = '彝'; break;
    case '08': result['mzmc'] = '壮'; break;
    case '09': result['mzmc'] = '布依'; break;
    case '10': result['mzmc'] = '朝鲜'; break;
    case '11': result['mzmc'] = '满'; break;
    case '12': result['mzmc'] = '侗'; break;
    case '13': result['mzmc'] = '瑶'; break;
    case '14': result['mzmc'] = '白'; break;
    case '15': result['mzmc'] = '土家'; break;
    case '16': result['mzmc'] = '哈尼'; break;
    case '17': result['mzmc'] = '哈萨克'; break;
    case '18': result['mzmc'] = '傣'; break;
    case '19': result['mzmc'] = '黎'; break;
    case '20': result['mzmc'] = '傈僳'; break;
    case '21': result['mzmc'] = '佤'; break;
    case '22': result['mzmc'] = '畲'; break;
    case '23': result['mzmc'] = '高山'; break;
    case '24': result['mzmc'] = '拉祜'; break;
    case '25': result['mzmc'] = '水'; break;
    case '26': result['mzmc'] = '东乡'; break;
    case '27': result['mzmc'] = '纳西'; break;
    case '28': result['mzmc'] = '景颇'; break;
    case '29': result['mzmc'] = '柯尔克孜'; break;
    case '30': result['mzmc'] = '土'; break;
    case '31': result['mzmc'] = '达翰尔'; break;
    case '32': result['mzmc'] = '仫佬'; break;
    case '33': result['mzmc'] = '羌'; break;
    case '34': result['mzmc'] = '布朗'; break;
    case '35': result['mzmc'] = '撒拉'; break;
    case '36': result['mzmc'] = '毛南'; break;
    case '37': result['mzmc'] = '仡佬'; break;
    case '38': result['mzmc'] = '锡伯'; break;
    case '39': result['mzmc'] = '阿昌'; break;
    case '40': result['mzmc'] = '普米'; break;
    case '41': result['mzmc'] = '塔吉克'; break;
    case '42': result['mzmc'] = '怒'; break;
    case '43': result['mzmc'] = '乌孜别克'; break;
    case '44': result['mzmc'] = '俄罗斯'; break;
    case '45': result['mzmc'] = '鄂温克'; break;
    case '46': result['mzmc'] = '德昂'; break;
    case '47': result['mzmc'] = '保安'; break;
    case '48': result['mzmc'] = '裕固'; break;
    case '49': result['mzmc'] = '京'; break;
    case '50': result['mzmc'] = '塔塔尔'; break;
    case '51': result['mzmc'] = '独龙'; break;
    case '52': result['mzmc'] = '鄂伦春'; break;
    case '53': result['mzmc'] = '赫哲'; break;
    case '54': result['mzmc'] = '门巴'; break;
    case '55': result['mzmc'] = '珞巴'; break;
    case '56': result['mzmc'] = '基诺'; break;
    case '59': result['mzmc'] = '穿青人'; break;
    case '60': result['mzmc'] = '革家人'; break;
    case '97': result['mzmc'] = '其它'; break;
    case '98': result['mzmc'] = '入籍'; break;
    case '99': result['mzmc'] = '其它'; break;
    default: result['mzmc'] = '';
  }
  return result;
}
var totalStartTime;
var totalReadCount;
var SuccessTimes = 0;
var FailTimes = 0;
var totalUsedTime = 0;
function startReadCard(){
    totalStartTime = new Date();
    totalReadCount = $("#totalReadCount").val();
    SuccessTimes = 0;
    FailTimes = 0;
    totalUsedTime = 0;
    readCard();
}

function readCard(){   
    clearForm();
    var startTime = new Date();
    $("#StartTime").val(fmtDate(startTime));
    $("#readCard").attr("disabled", "disabled");
    var readerIndex = $("#readerList").find("option:selected").attr("index");
    console.info("read card", readerIndex)

    $.ajax({
		type: "get",
		dataType: "json", 
        timeout:10000,
        url: baseUrl + "/readIDCard?index="+readerIndex,
        success: function(resultInfo) {
            console.info("readIDCard ", resultInfo, status);
            $("#output").text(JSON.stringify(resultInfo));
            if( resultInfo.result === 0 ){
                var textInfo = resultInfo.wzInfo;

                $("#text_name").val(textInfo.substr(0,15).trim());
                $("#text_genderid").val(textInfo.substr(15,1).trim());
                $("#text_nationid").val(textInfo.substr(16,2).trim());
                $("#text_birthdate").val(textInfo.substr(18,8).trim());
                $("#text_address").val(textInfo.substr(26,35).trim());
                $("#text_idnumber").val(textInfo.substr(61,18).trim());
                $("#text_signorgan").val(textInfo.substr(79,15).trim());
                $("#text_beginterm").val(textInfo.substr(94,8).trim());
                $("#text_endterm").val(textInfo.substr(102,8).trim());

                var wzObj = parseCardWzxx(textInfo);
                $("#text_nation").val(wzObj.mzmc);

                console.info("read card success", wzObj) ;

                $.ajax({
					type: "get",
					dataType: "json", 
					timeout:10000,
                    url:baseUrl +"/wltUnpack?wlt="+resultInfo.zpWlt+"&format=bmp",
                    success:function(response){
                        if(response['result'] === 0){
                            var imageBase64 = response['image'];
                            $("#text_photobase64").val(imageBase64);
                            document.all['PhotoDisplay'].src = 'data:image/bmp;base64,' + imageBase64;
                        }else{
                          alert("unpack error " + response['result']);
                        }
                    },
                    complete: function(XMLHttpRequest, textStatus) {
                    }
                });
            } 
            var endTime = new Date();
            $("#EndTime").val(fmtDate(endTime));
            var usedTime = endTime.getTime()-startTime.getTime();
            $("#ReadTime").val(usedTime + "ms");
            
            if ( $('#autoReadCard').is(":checked") && totalReadCount--> 0){
                self.setTimeout("readCard()", 1000);
                totalUsedTime +=  usedTime;
                $("#TimeExpand").val(totalUsedTime + "ms");
                if(resultInfo.result == 0){
                    ++SuccessTimes; 
                }else{
                    ++FailTimes;
                }
                $("#SuccessTimes").val(SuccessTimes);
                $("#FailTimes").val(FailTimes);
                $("#Rate").val(SuccessTimes*100/(SuccessTimes+FailTimes) + "%");
                $("#AvgTime").val(totalUsedTime/(SuccessTimes+FailTimes) + "ms");

            }else{
                $("#readCard").removeAttr("disabled");
            }

        },
        complete: function(XMLHttpRequest, textStatus) {
//            alert("cloudReadCard/appId complete");
            console.info("complete");
        },
        error: function(jqXHR, textStatus, errorThrown) {
            console.error("read card request error  ", textStatus, errorThrown );
            $("#output").text("read card error status "+ textStatus+" errorThrown "  +errorThrown)
            $("#readCard").removeAttr("disabled");

        }
    });
}


</script>

二、JAVA

import com.bland.IDReader;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.Base64;

import java.io.*;

public class demo{
	
    static public String race_table[][] ={
        {"01","汉族"},//   GB/T 3304-1991","Han","HA
        {"02","蒙古族"},//   GB/T 3304-1991","Mongol","MG
        {"03","回族"},//   GB/T 3304-1991","Hui","HU
        {"04","藏族"},//   GB/T 3304-1991","Zang","ZA
        {"05","*"},//   GB/T 3304-1991","Uygur","uG
        {"06","苗族"},//   GB/T 3304-1991","Miao","MH
        {"07","彝族"},//   GB/T 3304-1991","Yi","YI
        {"08","壮族"},//   GB/T 3304-1991","Zhuang","ZH
        {"09","布依族"},//   GB/T 3304-1991","Buyei","BY
        {"10","朝鲜族"},//   GB/T 3304-1991","Chosen","cs
        {"11","满族"},//   GB/T 3304-1991","Man","MA
        {"12","侗族"},//   GB/T 3304-1991","Dong","DO
        {"13","瑶族"},//   GB/T 3304-1991","Yao","YA
        {"14","白族"},//   GB/T 3304-1991","Bai","BA
        {"15","土家族"},//   GB/T 3304-1991","Tujia","TJ
        {"16","哈尼族"},//   GB/T 3304-1991","Hani","HN
        {"17","哈萨克族"},//   GB/T 3304-1991","Kazak","KZ
        {"18","傣族"},//   GB/T 3304-1991","Dai","DA
        {"19","黎族"},//   GB/T 3304-1991","Li","LI
        {"20","傈僳族"},//   GB/T 3304-1991","Lisu","LS
        {"21","佤族"},//   GB/T 3304-1991","Va","VA
        {"22","畲族"},//   GB/T 3304-1991","She","SH
        {"23","高山族"},//   GB/T 3304-1991","Gaoshan","Gs
        {"24","拉枯族"},//   GB/T 3304-1991","Lahu","LH
        {"25","水族"},//   GB/T 3304-1991","Sui","su
        {"26","东乡族"},//   GB/T 3304-1991","Dongxiang","DX
        {"27","纳西族"},//   GB/T 3304-1991","Naxi","NX
        {"28","景颇族"},//   GB/T 3304-1991","Jingpo","JP
        {"29","柯尔克孜族"},//   GB/T 3304-1991","Kirgiz","KG
        {"30","土族"},//   GB/T 3304-1991","Tu","TU
        {"31","达斡尔族"},//   GB/T 3304-1991","Daur","DU
        {"32","仫佬族"},//   GB/T 3304-1991","Mulao","ML
        {"33","羌族"},//   GB/T 3304-1991","Qiang","QI
        {"34","布朗族"},//   GB/T 3304-1991","Blang","BL
        {"35","撒拉族"},//   GB/T 3304-1991","Salar","SL
        {"36","毛南族"},//   GB/T 3304-1991","Maonan","MN
        {"37","仡佬族"},//   GB/T 3304-1991","Gelao","GL
        {"38","锡伯族"},//   GB/T 3304-1991","Xibe","XB
        {"39","阿昌族"},//   GB/T 3304-1991","Achang","AC
        {"40","普米族"},//   GB/T 3304-1991","Pumi","PM
        {"41","塔吉克族"},//   GB/T 3304-1991","Tajik","TA
        {"42","怒族"},//   GB/T 3304-1991","Nu","NU
        {"43","乌孜别克族"},//   GB/T 3304-1991","Uzbek","Uz
        {"44","俄罗斯族"},//   GB/T 3304-1991","Russ","RS
        {"45","鄂温克族"},//   GB/T 3304-1991","Ewenki","EW
        {"46","德昂族"},//   GB/T 3304-1991","Deang","DE
        {"47","保安族"},//   GB/T 3304-1991","Bonan","BN
        {"48","裕固族"},//   GB/T 3304-1991","Yugur","YG
        {"49","京族"},//   GB/T 3304-1991","Gin","GI
        {"50","塔塔尔族"},//   GB/T 3304-1991","Tatar","TT
        {"51","独龙族"},//   GB/T 3304-1991","Derung","DR
        {"52","鄂伦春族"},//   GB/T 3304-1991","Oroqen","OR
        {"53","赫哲族"},//   GB/T 3304-1991","Hezhen","HZ
        {"54","门巴族"},//   GB/T 3304-1991","Monba","MB
        {"55","珞巴族"},//   GB/T 3304-1991","Lhoba","LB
        {"56","基诺族"},//   GB/T 3304-1991","Jino","JN
        {"",""}
    };

    /**
     * 将民族代码,转换成文本
     * @param code
     * @return
     */
    public static String GetRace(String code) {
        int i;
        for (i = 0; i < race_table.length; i++) {
            if (race_table[i][0].compareTo(code) == 0) {
                return race_table[i][1];
            }
        }
        return "其它";
    }

    /**
     * 显示身份证信息
     * @param text 文本
     * @param image 照片
     */
    public static void ViewInfo(String text,byte[] image) {
        System.out.println(text);
		try
		{
			File imgFile = new File("image.jpg"); 
			FileOutputStream writer = new FileOutputStream(imgFile); 
			writer.write(image); 
			writer.close(); 
		}
		catch (Exception e)
		{
			e.printStackTrace(); 
		}
    }


    /**
     * 解析SN
     * @param str JSON 格式字符串
     * @return 返回错误或成功信息
     */
    public static int ParseSN(String str) {
        byte[] img;
        String info = "";

        try {
            JSONObject jsondata = new JSONObject(str);

            if(jsondata.has("errorMsg")) {
				//System.out.println(jsondata.getString("errorMsg"));
                return -1;
            }

            if( !jsondata.has("data") ) {
                return -1;
            }

            JSONObject content = jsondata.getJSONObject("data");

			System.out.println("SAM ID: " + content.getString("samid"));
			if(content.has("sn")) {
				System.out.println("SN: " + content.getString("sn"));
			}
		}
		catch (Exception e) {
			e.printStackTrace(); 
            return -1;
        }
		return 0;
	}

    /**
     * 解析身份证读卡结果
     * @param str JSON 格式字符串
     * @return 返回错误或成功信息
     */
    public static String ParseIDInfo(String str) {
        byte[] img;
        String info = "";

        try {
            JSONObject jsondata = new JSONObject(str);

            if(jsondata.getString("function").compareTo("readcard") != 0) {
                return "";//不是读卡消息,直接返回
            }

            if(jsondata.has("errorMsg")) {
				System.out.println(jsondata.getString("errorMsg"));
                return jsondata.getString("errorMsg");
            }

            if( !jsondata.has("data") ) {
                return "";
            }

            JSONObject content = jsondata.getJSONObject("data");

			if(content.getString("type").compareTo("I") == 0) { 
				info = "外国人永久居留证";
			}
			else if(content.getString("type").compareTo("J") == 0) { 
				info = "港澳台居住证";
			}
			else if(content.getString("type").compareTo(" ") == 0) { 
				info = "居民身份证";
			}
			else  {
				info = "未知";
			}

            info = info + "\r\n姓名:" + content.getString("name");
            info = info + "\r\n身份证号:" + content.getString("number");
            String gender;
            if(content.getInt("gender") == 1) {
                gender = "男";
            }
            else if (content.getInt("gender") == 2){
                gender = "女";
            }
            else {
                gender = "未知";
            }
            info = info + "\r\n性别: " + gender;
			
			if(content.getString("type").compareTo(" ") == 0) { 
				//只有身份证有民族信息
	            info = info + "\r\n民族: " + GetRace(content.getString("race"));
			}

            info = info + "\r\n生日: " + content.getString("birthday");

			if(content.getString("type").compareTo("I") != 0 ) { 
				//外国人永久居留证没有住址信息
	            info = info + "\r\n住址: " + content.getString("address");
			}

            info = info + "\r\n签发机关: " + content.getString("issuer");
            info = info + "\r\n有效期限: " + content.getString("valied") + "-" + content.getString("expire");
			
			if(content.getString("type").compareTo("I") == 0) { 
				//外国人永久居留证
	            info = info + "\r\n中文名称: " + content.getString("cn_name");
				info = info + "\r\n版本: " + content.getString("version");
				info = info + "\r\n国籍: " + content.getString("nation");
			}
			else if(content.getString("type").compareTo("J") == 0) { 
				//港澳台居住证
	            info = info + "\r\n通行证号码: " + content.getString("pass_number");
				info = info + "\r\n签发次数: " + content.getString("issue_count");
			}

			info = info + "\r\n";

            img = Base64.getDecoder().decode(content.getString("photo"));
			ViewInfo(info,img);
            return "读卡成功!";
        }
        catch (Exception e) {
			e.printStackTrace(); 
            return e.getMessage();
        }

    }

    /**
     * 解析银行卡读卡结果
     * @param str JSON 格式字符串
     * @return 返回错误或成功信息
     */
    public static String ParseBankInfo(String str) {
        byte[] img;
        String info = "";

        try {
            JSONObject jsondata = new JSONObject(str);

            if(jsondata.has("errorMsg")) {
				System.out.println(jsondata.getString("errorMsg"));
                return jsondata.getString("errorMsg");
            }

            if( !jsondata.has("data") ) {
                return "";
            }

            JSONObject content = jsondata.getJSONObject("data");


            info = info + "\r\n银行卡号:" + content.getString("number");
            info = info + "\r\n有效期截止: " + content.getString("expire");
			
            info = info + "\r\n持卡人:" + content.getString("owner");
            info = info + "\r\n持卡人证件号码: " + content.getString("owner_id");

			info = info + "\r\n";
			
			System.out.println(info);

            return "读卡成功!";
        }
        catch (Exception e) {
			e.printStackTrace(); 
            return e.getMessage();
        }

	}

    /**
     * 解析读卡结果的返回值
     * @param str JSON 格式字符串
     * @return 返回读卡结果的返回值
     */
	public static int GetRetValue(String str) {
        try {
            JSONObject jsondata = new JSONObject(str);
			if(jsondata.has("ret")) {
				return jsondata.getInt("ret");
			}
			else {
				return -1;
			}
		}
        catch (Exception e) {
			e.printStackTrace(); 
            return -1;
        }
	}

	public static void main(String [] args) {
		String systemType = System.getProperty("os.name");   
		String systemArch = System.getProperty("os.arch");   

		IDReader reader = new IDReader();

		System.out.println("System Type: " + systemType + ", " + systemArch);

		//提供一个可读写的目录,用于初始化过程中读写配置,解压调用库文件。初始化只能调用一次。

		reader.Init("./res");
		System.out.println("demo start!");

		try
		{
			while(true) {
				Thread.sleep(100);
				String result = reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"getsam\"}");
				if(ParseSN(result) == 0) {
					break;
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace(); 
		}

		try
		{

			while(true) {
				String result;

				result = reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"readcard\"}");
				if( GetRetValue(result) == 0) {
					ParseIDInfo(result);
					Thread.sleep(1000);
					continue;
				}

				result = reader.WebSocketAPI("{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"iso14443a_find\"}");
				if( GetRetValue(result) == 0) {
					//读银行卡号码
					result = reader.WebSocketAPI("{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"pboc_read\"}");
					ParseBankInfo(result);
					Thread.sleep(1000);
					continue;
				}
				Thread.sleep(100);
			}
		}
		catch (Exception e)
		{
			e.printStackTrace(); 
		}

		
	}
}

三、PYTHON

# -*- coding: utf-8 -*-
# import urllib2
import urllib.request
import json
import time

race = ["","汉族","蒙古族","回族","藏族","*","苗族","彝族","壮族","布依族","朝鲜族","满族","侗族","瑶族","白族","土家族","哈尼族","哈萨克族","傣族","黎族","傈僳族","佤族","畲族","高山族","拉枯族","水族","东乡族","纳西族","景颇族","柯尔克孜族","土族","达斡尔族","仫佬族","羌族","布朗族","撒拉族","毛南族","仡佬族","锡伯族","阿昌族","普米族","塔吉克族","怒族","乌孜别克族","俄罗斯族","鄂温克族","德昂族","保安族","裕固族","京族","塔塔尔族","独龙族","鄂伦春族","赫哲族","门巴族","珞巴族" ,"基诺族"]
gender = ["未知","男","女","其他"]


while 1:
    time.sleep(1)
    url = "http://127.0.0.1:7846/api/readCard?utf8=1"

    response = urllib.request.urlopen(url)
    page_html = response.read()
    #print(page_html)

    result = json.loads(page_html)

    if result["resultFlag"] == "0":
        print("读卡成功:\n")
        print("身份证号码:"+result["resultContent"]["idNum"])
        print("姓名:"+result["resultContent"]["name"])
        print("性别:"+gender[int(result["resultContent"]["gender"])])
        print("民族:"+race[int(result["resultContent"]["nation"])])
        print("地址:"+result["resultContent"]["address"])
        print("生日:"+result["resultContent"]["birthday"])
        print("有效期: "+result["resultContent"]["effectDate"] + "-" + result["resultContent"]["expireDate"] )
        print("签发机关: "+result["resultContent"]["issueOrg"])
        continue

    url = "http://127.0.0.1:7846/readBankCard"
    response = urllib.request.urlopen(url)
    page_html = response.read()
    #print(page_html)

    result = json.loads(page_html)
    if result["result"] == 0 :
        print("读卡成功:\n")
        print("银行卡号码:"+result["number"])
        print("有效期截止:"+result["expire"])
        print("持卡人:"+result["owner"])
        print("持卡人证件号码:"+result["owner_id"])

    

四、VB

Public Class Form1
	Dim port As reader_serviceLib.sdt = New reader_serviceLib.sdt()
	Dim card As reader_serviceLib.card

	Private Sub refreshButton()
		Dim ret As Integer

		ret = port.opened()

		If ret = 0 Then
			btOpen.Enabled = True
			btReadCard.Enabled = False
			btReadBank.Enabled = False
			btOpen.Text = "打开"
		Else
			btOpen.Enabled = True
			btReadCard.Enabled = True
			btReadBank.Enabled = True
			btOpen.Text = "关闭"
			If ret > 1000 Then
				TextStatus.Text = "已打开USB端口" & ret
			Else
				TextStatus.Text = "已打开串口COM" & ret
			End If
			TextSAMID.Text = port.samid()
		End If

	End Sub


	Private Sub btReadCard_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btReadCard.Click
		Dim ret As Integer


		ret = port.find()
		If ret <> &H9F Then
			TextStatus.Text = "找卡失败!"
			Exit Sub
		End If

		ret = port.select()
		If ret <> &H90 Then
			TextStatus.Text = "选卡失败!"
			Exit Sub
		End If

		TextCardSN.Text = port.cardSN(0)

		card = port.readcard(0)
		If card.name = "" Then
			TextStatus.Text = "读卡失败!"
			Exit Sub
		End If

		TextName.Text = card.name
		TextAddress.Text = card.address
		TextBirthday.Text = card.birthday
		TextGender.Text = card.gender
		TextIssuer.Text = card.issuer
		TextRace.Text = card.race
		TextNumber.Text = card.number
		TextExpired.Text = card.valied & "-" & card.expire

		PictureBox1.ImageLocation = card.image_path



	End Sub

	Private Sub btOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btOpen.Click
		Dim ret As Integer

		If ComboPortList.Text = "USB" Then
			ret = port.open(1001, 115200)
		ElseIf ComboPortList.Text.StartsWith("COM") Then
			ret = port.open(Integer.Parse(ComboPortList.Text.Substring(3)), 115200)
		Else
			ret = port.open(0, 0)
		End If

		If ret <> &H90 Then
			TextStatus.Text = "端口打开失败!"
			Exit Sub
		End If

		Call refreshButton()
	End Sub

	Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
		refreshButton()
	End Sub

	Private Sub btReadBank_Click(sender As Object, e As EventArgs) Handles btReadBank.Click
		Dim uid As reader_serviceLib.iso14443a_uid
		Dim obj()
		Dim ret As Integer

		obj = port.iso14443a_find(4)

		If obj.Length = 0 Then
			TextStatus.Text = "找卡失败!"
			' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
			Call port.iso14443a_mode_exit()
			Exit Sub
		End If

		uid = obj(0)

		ret = port.iso14443a_select(uid.text, uid.sak)

		If ret <> &H90 Then
			' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
			Call port.iso14443a_mode_exit()
			Call MsgBox("选卡失败!")
			Exit Sub
		End If

		'参数0代表读身份证,参数1代表读银行卡
		card = port.readcard(1)
		If card.number = "" Then
			' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
			Call port.iso14443a_mode_exit()
			Call MsgBox("读卡失败!")
			Exit Sub
		End If

		Call MsgBox("银行卡号:" & card.number & ";有效期:" & card.expire & ";持卡人:" & card.name)


		' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
		Call port.iso14443a_mode_exit()

	End Sub
End Class

五、安卓

package com.example.demo;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;

import com.bland.IDReader;
import com.bland.TpNfc;
import com.google.android.material.snackbar.Snackbar;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Message;
import android.os.Handler;
import android.view.View;
import android.app.PendingIntent;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.widget.Toast;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import android.content.Intent;
import android.content.IntentFilter;
import com.example.demo.databinding.ActivityMainBinding;


import java.util.HashMap;

import android.view.Menu;
import android.view.MenuItem;
import android.nfc.tech.NfcB;

import org.json.JSONArray;
import org.json.JSONObject;
import 	android.provider.Settings;
import android.content.Context;
import android.nfc.Tag;
import android.nfc.NfcAdapter;
import android.util.Log;
import java.util.HashMap;
import android.util.Base64;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    //用于身份证解码先关变量
    public String race_table[][] ={
        {"01","汉族"},//   GB/T 3304-1991","Han","HA
        {"02","蒙古族"},//   GB/T 3304-1991","Mongol","MG
        {"03","回族"},//   GB/T 3304-1991","Hui","HU
        {"04","藏族"},//   GB/T 3304-1991","Zang","ZA
        {"05","*"},//   GB/T 3304-1991","Uygur","uG
        {"06","苗族"},//   GB/T 3304-1991","Miao","MH
        {"07","彝族"},//   GB/T 3304-1991","Yi","YI
        {"08","壮族"},//   GB/T 3304-1991","Zhuang","ZH
        {"09","布依族"},//   GB/T 3304-1991","Buyei","BY
        {"10","朝鲜族"},//   GB/T 3304-1991","Chosen","cs
        {"11","满族"},//   GB/T 3304-1991","Man","MA
        {"12","侗族"},//   GB/T 3304-1991","Dong","DO
        {"13","瑶族"},//   GB/T 3304-1991","Yao","YA
        {"14","白族"},//   GB/T 3304-1991","Bai","BA
        {"15","土家族"},//   GB/T 3304-1991","Tujia","TJ
        {"16","哈尼族"},//   GB/T 3304-1991","Hani","HN
        {"17","哈萨克族"},//   GB/T 3304-1991","Kazak","KZ
        {"18","傣族"},//   GB/T 3304-1991","Dai","DA
        {"19","黎族"},//   GB/T 3304-1991","Li","LI
        {"20","傈僳族"},//   GB/T 3304-1991","Lisu","LS
        {"21","佤族"},//   GB/T 3304-1991","Va","VA
        {"22","畲族"},//   GB/T 3304-1991","She","SH
        {"23","高山族"},//   GB/T 3304-1991","Gaoshan","Gs
        {"24","拉枯族"},//   GB/T 3304-1991","Lahu","LH
        {"25","水族"},//   GB/T 3304-1991","Sui","su
        {"26","东乡族"},//   GB/T 3304-1991","Dongxiang","DX
        {"27","纳西族"},//   GB/T 3304-1991","Naxi","NX
        {"28","景颇族"},//   GB/T 3304-1991","Jingpo","JP
        {"29","柯尔克孜族"},//   GB/T 3304-1991","Kirgiz","KG
        {"30","土族"},//   GB/T 3304-1991","Tu","TU
        {"31","达斡尔族"},//   GB/T 3304-1991","Daur","DU
        {"32","仫佬族"},//   GB/T 3304-1991","Mulao","ML
        {"33","羌族"},//   GB/T 3304-1991","Qiang","QI
        {"34","布朗族"},//   GB/T 3304-1991","Blang","BL
        {"35","撒拉族"},//   GB/T 3304-1991","Salar","SL
        {"36","毛南族"},//   GB/T 3304-1991","Maonan","MN
        {"37","仡佬族"},//   GB/T 3304-1991","Gelao","GL
        {"38","锡伯族"},//   GB/T 3304-1991","Xibe","XB
        {"39","阿昌族"},//   GB/T 3304-1991","Achang","AC
        {"40","普米族"},//   GB/T 3304-1991","Pumi","PM
        {"41","塔吉克族"},//   GB/T 3304-1991","Tajik","TA
        {"42","怒族"},//   GB/T 3304-1991","Nu","NU
        {"43","乌孜别克族"},//   GB/T 3304-1991","Uzbek","Uz
        {"44","俄罗斯族"},//   GB/T 3304-1991","Russ","RS
        {"45","鄂温克族"},//   GB/T 3304-1991","Ewenki","EW
        {"46","德昂族"},//   GB/T 3304-1991","Deang","DE
        {"47","保安族"},//   GB/T 3304-1991","Bonan","BN
        {"48","裕固族"},//   GB/T 3304-1991","Yugur","YG
        {"49","京族"},//   GB/T 3304-1991","Gin","GI
        {"50","塔塔尔族"},//   GB/T 3304-1991","Tatar","TT
        {"51","独龙族"},//   GB/T 3304-1991","Derung","DR
        {"52","鄂伦春族"},//   GB/T 3304-1991","Oroqen","OR
        {"53","赫哲族"},//   GB/T 3304-1991","Hezhen","HZ
        {"54","门巴族"},//   GB/T 3304-1991","Monba","MB
        {"55","珞巴族"},//   GB/T 3304-1991","Lhoba","LB
        {"56","基诺族"},//   GB/T 3304-1991","Jino","JN
        {"",""}
    };
    private static final int SDK_NFC_FLAG = 3;
    private static final int SDK_READER_FLAG = 4;
    private static final int SDK_DEVICE_FLAG = 5;
    private static final int SDK_CLIENT_FLAG = 6;
    private static final int SDK_ICCARD_FLAG = 7;
    private static final int SDK_BANKCARD_FLAG = 8;

    //用于 OTG 阅读器相关变量
    public IDReader reader;
    public UsbManager usbManager;
    private static final String ACTION_USB_PERMISSION = "com.android.usb.USB_PERMISSION";
    private boolean id_card_exist = false;      // 标记上一次读卡时,身份证是否存在
    private String samid = "";                      // 标记阅读器是否连接,以及SAM ID是多少
    private String last_idcard_text = "";           // 最近一次身份证读卡信息,方便判断身份证是否变化
    private String last_iccard_text = "";           // 最近一次Mifare one读卡数据信息
    private String last_iccard_uid = "";           // 最近一次Mifare one 读卡UID信息
    private int last_iccard_sak = 0;           // 最近一次Mifare one 读卡SAK信息
    //用于手机本机 NFC 相关变量
    public TpNfc tpnfc;
    NfcAdapter mNfcAdapter;
    PendingIntent pIntent;

    //其他变量
    private AppBarConfiguration appBarConfiguration;
    private ActivityMainBinding binding;

    /**
     * 将民族代码,转换成文本
     * @param code
     * @return
     */
    public String GetRace(String code) {
        int i;
        for (i = 0; i < race_table.length; i++) {
            if (race_table[i][0].compareTo(code) == 0) {
                return race_table[i][1];
            }
        }
        return "其它";
    }

    /**
     * 在 UI 上显示身份证信息
     * @param text 文本
     * @param image 照片
     */
    void ViewInfo(String text,Bitmap image) {
        TextView tv = (TextView) findViewById(R.id.textview_first);
        ImageView iv = (ImageView) findViewById(R.id.imageView);

        tv.setText(text.toCharArray(), 0, text.toCharArray().length);
        iv.setImageBitmap(image);
    }

    /**
     * 解析身份证读卡结果
     * @param str JSON 格式字符串
     * @return 返回错误或成功信息
     */
    String ParseInfo(String str) {
        Bitmap decodedByte = null;
        String info = "";

        try {
            JSONObject jsondata = new JSONObject(str);

            if(jsondata.getString("function").compareTo("readcard") != 0) {
                return "";//不是读卡消息,直接返回
            }

            if(jsondata.has("errorMsg")) {
                ViewInfo(info,decodedByte);  //卡片已拿走或其他错误,清空信息
                return jsondata.getString("errorMsg");
            }

            if( !jsondata.has("data") ) {
                //异常情况,不应该出现,清空信息
                ViewInfo(info,decodedByte);
                return "";
            }

            JSONObject content = jsondata.getJSONObject("data");
            if(content.getString("type").compareTo("I") == 0) {
                info = "2017版外国人永久居留证";
            }
            else if(content.getString("type").compareTo("Y") == 0) {
                info = "新版外国人永久居留证";
            }
            else if(content.getString("type").compareTo("J") == 0) {
                info = "港澳台居住证";
            }
            else if(content.getString("type").compareTo(" ") == 0) {
                info = "居民身份证";
            }
            else  {
                info = "未知";
            }

            info = info + "\r\n姓名:" + content.getString("name");
            info = info + "\r\n身份证号:" + content.getString("number");
            String gender;
            if(content.getInt("gender") == 1) {
                gender = "男";
            }
            else if (content.getInt("gender") == 2){
                gender = "女";
            }
            else {
                gender = "未知";
            }
            info = info + "\r\n性别: " + gender;

            if(content.getString("type").compareTo(" ") == 0) {
                //只有身份证有民族信息
                info = info + "\r\n民族: " + GetRace(content.getString("race"));
            }

            info = info + "\r\n生日: " + content.getString("birthday");

            if(content.getString("type").compareTo("I") != 0 ) {
                //外国人永久居留证没有住址信息
                info = info + "\r\n住址: " + content.getString("address");
            }

            info = info + "\r\n签发机关: " + content.getString("issuer");
            info = info + "\r\n有效期限: " + content.getString("valied") + "-" + content.getString("expire");

            if(content.getString("type").compareTo("I") == 0) {
                //2017版外国人永久居留证
                info = info + "\r\n中文名称: " + content.getString("cn_name");
                info = info + "\r\n版本: " + content.getString("version");
                info = info + "\r\n国籍: " + content.getString("nation");
            }
            else if(content.getString("type").compareTo("Y") == 0) {
                //新版外国人永久居留证
                info = info + "\r\n中文名称: " + content.getString("cn_name");
                info = info + "\r\n换证次数: " + content.getString("renewal_count");
                info = info + "\r\n关联号码: " + content.getString("relational_number");
                info = info + "\r\n国籍: " + content.getString("nation");
            }
            else if(content.getString("type").compareTo("J") == 0) {
                //港澳台居住证
                info = info + "\r\n通行证号码: " + content.getString("pass_number");
                info = info + "\r\n签发次数: " + content.getString("issue_count");
            }

            byte[] img = Base64.decode(content.getString("photo"),Base64.DEFAULT);
            decodedByte = BitmapFactory.decodeByteArray(img, 0, img.length);

            ViewInfo(info,decodedByte);
            return "读卡成功!";
        }
        catch (Exception e) {
            return e.getMessage();
        }

    }

    /**
     * 处理身份证阅读消息,或者 OTG 阅读器插入拔出消息
     */
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public  void handleMessage(Message msg) {
            String err = "";
            switch (msg.what) {
                case SDK_NFC_FLAG: {
                    err = ParseInfo((String)msg.obj);
                    break;
                }
                case SDK_READER_FLAG: {
                    err = ParseInfo((String)msg.obj);
                    break;
                }
                case SDK_DEVICE_FLAG:{
                    if(samid == "" ) {
                        err = "阅读器已拔出!";
                    }
                    else {
                        err = "阅读器已连接:" + samid;
                    }
                    break;
                }
                case SDK_CLIENT_FLAG: {
                    ViewInfo((String)msg.obj,null);
                    break;
                }
                case SDK_ICCARD_FLAG: {
                    if(last_iccard_uid != "") {
                        ViewInfo("uid: " + last_iccard_uid + "\r\ndata: " + last_iccard_text, null);
                    }
                    else {
                        ViewInfo("",null);
                    }
                    break;
                }
                case SDK_BANKCARD_FLAG: {
                    if(last_iccard_uid != "") {
                        ViewInfo("uid: " + last_iccard_uid + "\r\n" + (String)msg.obj, null);
                    }
                    else {
                        ViewInfo("",null);
                    }
                    break;
                }
            }

            if (err != "") {
                Snackbar.make(findViewById(R.id.fab), err, Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        }
    };

    /**
     * 申请 USB OTG 阅读器访问权限
     * @param usbManager
     * @return
     */
    public int UsbPermission(UsbManager usbManager) {
        //单独申请USB权限
        PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
        HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
        for (UsbDevice usbDevice : deviceList.values()) {
            if( reader.CompareReaderID(usbDevice.getVendorId(),usbDevice.getProductId()) ) {
                if(!usbManager.hasPermission(usbDevice)) {
                    usbManager.requestPermission(usbDevice, permissionIntent);
                }
                else {

                }
                Log.e("id_reader","Reader found!");
                break;
            }
            else {
            }
            Log.e("id_reader","Name: " + usbDevice.getManufacturerName() + " " + usbDevice.getProductName() + ", VID: " + usbDevice.getVendorId() + ",PID: " + usbDevice.getProductId());
        }
        return 0;
    }

    /**
     * 发送消息到 UI
     * @param str
     */
    public void SendMsg(String str,int type) {
        Message msg = new Message();
        msg = new Message();
        if(mHandler != null) {
            msg.what = type;
            msg.obj = str;
            mHandler.sendMessage(msg);
        }
    }

    /**
     * 发送 OTG 阅读器连接或拔出的消息到 UI
     * @param samid
     */
    public void SendDeviceMsg(String samid) {
        SendMsg(samid,SDK_DEVICE_FLAG);
    }

    /**
     * 发送身份证读卡结果消息到 UI
     * @param result 身份证读卡结果,JSON格式
     * @param type
     */
    public void SendCardInfoMsg(String result,int type) {
        //tpnfc.SetLastRead(result);
        SendMsg(result,type);
    }

    /**
     * 判断 OTG 阅读器身份证读卡结果是否有变化,以决定是否需要发送消息到 UI
     * @param result
     * @return 返回身份证是否存在于OTG 阅读器
     */
    public boolean OnIDCardResult(String result) {
        try {
            boolean cur_exist;

            JSONObject jsondata = new JSONObject(result);
            if(jsondata.getInt("ret") != 0 ) {
                if (jsondata.getInt("ret") == 2) {
                    //阅读器设备状态改变,变为不存在,发送通知消息
                    samid = "";
                    SendDeviceMsg(samid);
                }
                last_idcard_text = "";
                cur_exist = false;
            }
            else {
                cur_exist = true;
            }

            if(jsondata.has("data")) {
                JSONObject data = jsondata.getJSONObject("data");
                if(data.has("text")) {
                    String cur_text = data.getString("text");
                    if (cur_text.compareTo(last_idcard_text) != 0) {//卡片信息发生改变,发送通知消息
                        id_card_exist = true;
                        last_idcard_text = cur_text;
                        SendCardInfoMsg(result, SDK_READER_FLAG);
                        return id_card_exist;
                    }
                }
            }

            if(cur_exist != id_card_exist) {//卡片状态发生改变,发送通知消息
                id_card_exist = cur_exist;
                SendCardInfoMsg(result, SDK_READER_FLAG);
                return id_card_exist;
            }

        }
        catch (Exception e) {
            id_card_exist = false;
        }

        return id_card_exist;
    }


    /**
     * 判断 OTG Mifare one 读卡结果是否有变化,以决定是否需要发送消息到 UI
     * @param result
     * @return 返回Mifare one 卡是否存在
     */
    public boolean OnICCardResult(String result) {
        try {

            JSONObject jsondata = new JSONObject(result);
            if(jsondata.getInt("ret") != 0 ) {
                if (jsondata.getInt("ret") == 2) {
                    //阅读器设备状态改变,变为不存在,发送通知消息
                    samid = "";
                    SendDeviceMsg(samid);
                }
                last_iccard_text = "";
                last_iccard_sak = 0;
            }
            String cmd = jsondata.getString("function");
            if(jsondata.has("data")) {
                JSONObject data = jsondata.getJSONObject("data");
                if( cmd.compareTo("iso14443a_find") == 0) {//处理找卡结果
                    JSONArray uid = data.getJSONArray("uid");
                    if(uid.length()>0) {
                        last_iccard_uid = uid.getJSONObject(0).getString("text");
                        last_iccard_sak = uid.getJSONObject(0).getInt("sak");
                        return true;
                    }
                    else {
                    }
                }
                else if(cmd.compareTo("pboc_read") == 0 && data.has("number") ) {
                    if(last_iccard_text.compareTo(data.getString("number")) !=0 ) {
                        last_iccard_text = data.getString("number");
                        String bankInfo = "银行卡号:"+data.getString("number") + "\r\n";
                        bankInfo += "有效期截止:" + data.getString("expire") + "\r\n";
                        bankInfo += "持卡人:" + data.getString("owner") + "\r\n";
                        bankInfo += "持卡人证件号码:" + data.getString("owner_id") + "\r\n";
                        SendCardInfoMsg(bankInfo, SDK_BANKCARD_FLAG);
                    }
                    return true;
                }
                else if( data.has("hex") ){//处理读到的数据
                    if(last_iccard_text.compareTo(data.getString("hex")) !=0 ) {
                        reader.WebSocketAPI("{\"module\":\"common\",\"msgid\":\"0\",\"function\":\"beep\",\"data\":{\"length\":200}}");
                        last_iccard_text = data.getString("hex");
                        SendCardInfoMsg(result, SDK_ICCARD_FLAG);
                    }
                    return true;
                }
            }
            if(last_iccard_text != "" || last_iccard_uid != "") {//卡片状态发生改变,发送通知消息
                last_iccard_uid = "";
                last_iccard_text = "";
                last_iccard_sak = 0;
                SendCardInfoMsg(result, SDK_ICCARD_FLAG);
            }
            return false;
        }
        catch (Exception e) {
            return false;
        }
    }

    /**
     * 清除读卡结果,并发送消息到 UI
     */
    public void ClearReaderResult() {
        if( samid.compareTo("") != 0 ){
            samid = "";
            SendDeviceMsg(samid);
        }

        if(id_card_exist) {
            id_card_exist = false;
            SendCardInfoMsg("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"readcard\",\"errorMsg\":\"设备不存在\",\"ret\":2}", SDK_READER_FLAG);
        }

        last_idcard_text = "";
    }

    /**
     * 读取 OTG 阅读器的 SAM ID,并返回 OTG 阅读器是否存在
     * @return 返回 OTG 阅读器是否存在
     */
    public boolean CheckSamID() {
        String result;
        JSONObject jsondata;
        try {
            if(samid == "" ) {
                result = reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"getsam\"}");
                jsondata = new JSONObject(result);
                if(jsondata.has("errorMsg") ) {
                    if(jsondata.has("ret") && jsondata.getInt("ret") == 2) {
                        //设备不存在
                    }
                    return false;
                }
                if(!jsondata.has("data") ) {
                    return false;
                }
                JSONObject data = jsondata.getJSONObject("data");
                if(!data.has("samid") ) {
                    return false;
                }
                samid = data.getString("samid");
                if(samid == "" ) {
                    return false;
                }
                SendDeviceMsg(samid);
            }
            return true;
        }
        catch(Exception e) {
            return false;
        }
    }

    /**
     * 使用 OTG 阅读器读取身份证,处理读取结果并返回身份证是否存在
     * @return 返回身份证是否存在
     */
    public boolean ReadIDCard() {
        try {
            if(!id_card_exist) {
                //还没有检测到卡片存在的情况下,可以先找卡和选卡。 这段代码不是必须,可以去掉,一样正常使用
                if( ! OnIDCardResult( reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"findcard\"}") ) ){
                    return false;
                }

                if( !OnIDCardResult( reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"selectcard\"}") ) ) {
                    return false;
                }
            }
            return OnIDCardResult( reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"readcard\"}") );
        }
        catch(Exception e) {
            return false;
        }
    }

    /**
     * 使用 OTG 阅读器读取Mifare one卡
     * @return 返回Mifare one卡是否存在
     */
    public boolean ReadICCard() {
        String cmd,pre_uid;

        if(id_card_exist) {//确认身份证卡片已存在的情况下,不读IC卡,避免冲突
            return false;
        }
        try {
            pre_uid = last_iccard_uid;

            if (!OnICCardResult(reader.WebSocketAPI("{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"iso14443a_find\"}"))) {
                return false;
            }

            if( last_iccard_uid.compareTo(pre_uid) == 0) {
                //卡没有发生变化,不重复读了
                return true;
            }

            if((last_iccard_sak & 0x20) == 0x20 ) {
                //构造读取银行卡号码的指令
                cmd = "{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"pboc_read\",\"data\": {\"uid\":\""+ last_iccard_uid + "\"} }";
            }
            else {
                //构造读取 IC 卡的第 0 个Block 的指令
                int type = 0x60;  //密钥A:0x60 , 密钥B:0x61
                String key = "FFFFFFFFFFFF";
                int block = 0;
                cmd = "{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"mifare_read\",\"data\": {\"uid\":\""+ last_iccard_uid + "\",\"block\":"+block+",\"type\":"+ type + ",\"key\":\""+key+"\"} }";
            }
            return OnICCardResult( reader.WebSocketAPI(cmd) );
        }
        catch(Exception e) {
            return false;
        }
    }

    /**
     * OTG 阅读器读取身份证信息线程
     */
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            while(true) {
                int  msgid = 0;
                try {
                    if( reader.Connected(usbManager) ) {
                        // 使用串口转网口模块时, reader.Connected(usbManager) 改为 true,直接 if(true) {
                        if(!CheckSamID()) {
                            Thread.sleep(1000);
                            continue;
                        }

                        if( ReadIDCard() || ReadICCard()) {
                            Thread.sleep(100);
                            continue;
                        }

                        Thread.sleep(100);

                    }
                    else {
                        ClearReaderResult();
                        Thread.sleep(200);
                        continue;
                    }

                }
                catch (Exception e) {
                    String error = e.toString();
                    Log.e("id_reader",error);
                }
            }
        }
    };


    /**
     *  感应到身份证时触发该事件。实现手机本机 NFC 读身份证。
     * @param intent
     */
    @Override
    protected void onNewIntent(Intent intent){
        super.onNewIntent(intent);
        setIntent(intent);

        Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if(tag == null ) {
            return ;
        }

        //sendCardInfoMsg("{\"module\" : \"tpnfc\",\"function\" : \"card\", \"data\": {\"id\":\"" + byte2hex(tag.getId()) + "\"},\"ret\":0}",SDK_NFC_FLAG);

        NfcB card = NfcB.get(tag);

        if(card == null ) {
            return;
        }

        try {
            card.connect();
            String result = tpnfc.LocalNfcRead(card);
            card.close();

            JSONObject content = new JSONObject(result);
            if(content.has("errorMsg")) {
                String error = content.getString("errorMsg");
                String s = new String(error.getBytes("ISO-8859-1"), "UTF-8");
                String info = "\nerror: " + s + "\n";
            }
            SendCardInfoMsg(result,SDK_NFC_FLAG);
        }
        catch (java.io.IOException e){
            String result = "{\"module\" : \"idcard\",\"function\" : \"readcard\",\"errorMsg\":\"" + e.getMessage() + "\",\"errorNo\":34, \"server_error_code\": 0 ,\"server_error_string\": \"\"}";
            SendCardInfoMsg(result,SDK_NFC_FLAG);
        }
        catch (Exception e){
            String result = "{\"module\" : \"idcard\",\"function\" : \"readcard\",\"errorMsg\":\"" + e.getMessage() + "\",\"errorNo\":34, \"server_error_code\": 0 ,\"server_error_string\": \"\"}";
            SendCardInfoMsg(result,SDK_NFC_FLAG);
        }
    }

    /**
     * 初始化手机本机 NFC 读身份证的相关变量和事件
     * @param path 提供可读写的路径,用于保存会话信息
     */
    public void InitNfc(String path){
        TpNfc.Init(path);
        tpnfc = new TpNfc();

        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (null == mNfcAdapter) {
            Toast.makeText(this, "不支持NFC功能", Toast.LENGTH_SHORT).show();
            TpNfc.nfc_support = 0;
        } else if (!mNfcAdapter.isEnabled()) {
            TpNfc.nfc_support = 0;
            Intent intent = new Intent(Settings.ACTION_NFC_SETTINGS);
            // 根据包名打开对应的设置界面
            startActivity(intent);
        }
        else {
            TpNfc.nfc_support = 1;
        }

        pIntent = PendingIntent.getActivity(this, 0,
                //在Manifest里或者这里设置当前activity启动模式,否则每次向阳NFC事件,activity会重复创建
                // 当然也要按照具体情况来,你设置成singleTask也不是不行,
                new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
                PendingIntent.FLAG_MUTABLE);
    }

    /**
     * 初始化 OTG 阅读器相关的变量、事件、权限等等
     * @param path 提供可读写的路径,用于保存配置
     */
    public void InitReader(String path){
        IDReader.ServiceStart(path);
        reader = new IDReader();

        usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

        UsbPermission(usbManager);

        new Thread(runnable).start();
    }

    /**
     * 修改手机本机 NFC 的相关设置
     */
    @Override
    protected void onResume() {
        super.onResume();
        if (mNfcAdapter != null) {
            //添加intent-filter
            IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
            IntentFilter tag = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
            IntentFilter tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
            IntentFilter[] filters = new IntentFilter[]{ndef, tag, tech};

            String[][] techList = new String[][]{
                    new String[]{
                            "android.nfc.tech.Ndef",
                            "android.nfc.tech.NfcA",
                            "android.nfc.tech.NfcB",
                            "android.nfc.tech.NfcF",
                            "android.nfc.tech.NfcV",
                            "android.nfc.tech.NdefFormatable",
                            "android.nfc.tech.MifareClassic",
                            "android.nfc.tech.MifareUltralight",
                            "android.nfc.tech.NfcBarcode"
                    }
            };
            mNfcAdapter.enableForegroundDispatch(this, pIntent, filters, techList);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mNfcAdapter != null) {
            mNfcAdapter.disableForegroundDispatch(this);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        String path = getFilesDir().toString();

        InitNfc(path);

        InitReader(path);

        setSupportActionBar(binding.toolbar);

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
        appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);


        binding.fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if(tpnfc == null ) {
                    return ;
                }

                /*
                    使用手机本机 NFC 阅读身份证时,需要先将手机与账号绑定。步骤如下:
                    1. 官网注册账号
                    2. 使用 tpnfc.SendBindRequest 向账号发送绑定请求
                    3. 官网登录账号,接受绑定请求
                    3. 调用 tpnfc.ClientInfo() 获取信息,判断是否绑定成功。
                    4. 绑定成功功后,即可正常使用手机NFC读取身份证信息
                 */

                String info = "";
                String user = "18118742537"; //用户账号
                try {
                    JSONObject jsondata = new JSONObject(tpnfc.ClientInfo());
                    if (jsondata.has("secret") && jsondata.has("limit") && jsondata.has("expire")) {
                        //已经绑定账号
                        info = "NFC客户端\n有效期:" + jsondata.getString("expire") + "\n次数限制:" + jsondata.getInt("decode") + "/" + jsondata.getInt("limit");
                        if(jsondata.getString("secret").compareTo("ok") != 0) {
                            //客户端认证失败,需要解除绑定后,再重新绑定
                            info += "\n密钥:密钥丢失,需要重新绑定";
                            info += "\n解除绑定:" + ParseServerError(tpnfc.Unbind(jsondata.getString("id"), user)); //解除绑定
                        }
                        else {

                        }
                    } else {
                        //还未绑定账号, 发送绑定请求, tpnfc.SendBindRequest 第一个参数为 客户端名称, 第二个参数是用户账号(手机号码)
                        info += "发送绑定请求:";
                        info += ParseServerError(tpnfc.SendBindRequest("测试NFC客户端", user));
                    }
                }
                catch ( Exception e) {
                    info = e.getMessage();
                }
                SendMsg(info,SDK_CLIENT_FLAG);
            }
        });
    }

    public String ParseServerError(