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

一、html5

html 复制代码
<!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

html 复制代码
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

html 复制代码
# -*- 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

html 复制代码
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

五、安卓

html 复制代码
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(String result) {
        String err = "";
        try {
            JSONObject ret = new JSONObject(result);
            if (ret.has("errorMsg")) {
                if (ret.getInt("errorNo") == 43) {
                    err = "操作失败:" + ret.getString("server_error_string");
                } else {
                    err = "操作失败:" + ret.getString("errorMsg");
                }
            } else {
                err = "操作成功!";
            }
        }
        catch (Exception e) {
            err = e.getMessage();
        }
        return err;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            //测试重启服务
            reader.ServiceStop();
            reader.ServiceStart(getFilesDir().toString());
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
        return NavigationUI.navigateUp(navController, appBarConfiguration)
                || super.onSupportNavigateUp();
    }


}

六、C#

cs 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace webapi_http
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btReadCard_Click(object sender, EventArgs e)
        {
            string result;
            string url = "http://127.0.0.1:7846/api/readCard?utf8=1";
            try
            {
                HttpWebRequest wbRequest = (HttpWebRequest)WebRequest.Create(url);
                wbRequest.Proxy = null;
                wbRequest.Method = "GET";
                HttpWebResponse wbResponse = (HttpWebResponse)wbRequest.GetResponse();
                using (Stream responseStream = wbResponse.GetResponseStream())
                {
                    using (StreamReader sReader = new StreamReader(responseStream))
                    {
                        result = sReader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                result = ex.Message;     //输出捕获到的异常,用OUT关键字输出
            }
            textResult.Text = result;
        }
    }
}

七、C

cs 复制代码
#include <stdio.h>
#include <sdtapi.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>


#ifndef WIN32
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>

void Sleep(unsigned int x)  
{ 
	struct timespec xsleep; \
	xsleep.tv_sec = x / 1000; \
	xsleep.tv_nsec = (x - xsleep.tv_sec * 1000) * 1000 * 1000; \
	nanosleep(&xsleep, NULL); \
}
#else
#include <windows.h>
#include <winnls.h>

#endif

#include "bland.h"


#ifndef WIN32
void print_com_list()//for linux
{
	struct dirent *pDirent;
	DIR *pDir;
	int i,index;
	struct stat st;
	char path[512] = {0};

	pDir = opendir("/sys/class/tty");
	if (pDir == NULL) {
		return ;
	}

	for (i=0,index=1;;i++) {
		pDirent = readdir(pDir);
		if(pDirent == NULL) {
			break;
		}
		snprintf(path,sizeof(path)-1,"/sys/class/tty/%s/device",pDirent->d_name);
		
		if (lstat(path, &st) < 0) {
			continue;
		}
		printf("COM%d: %s\n",index,path);
		index++;
	}
	closedir(pDir);
}

//使用 UTF-8 字符编码
#define GetName			GetNameUtf8
#define GetGender		GetGenderUtf8
#define GetBirthday		GetBirthdayUtf8
#define GetAddress		GetAddressUtf8
#define GetRace			GetRaceUtf8
#define GetIDNum		GetIDNumUtf8
#define GetIssuer		GetIssuerUtf8
#define GetValidFrom	GetValidFromUtf8
#define GetExpireDate	GetExpireDateUtf8
#define GetPassNum		GetPassNumUtf8
#define GetIssueCount	GetIssueCountUtf8
#define GetNationCode	GetNationCodeUtf8
#define GetCnName		GetCnNameUtf8


#endif


void print_info2(char * text)
{
	char str[256] = {0};
	char str2[256] = {0};
	int ret,type;

	type = GetType(text);

	ret = GetName(text,str,sizeof(str));
	if(ret < 0) {
		goto info_error;
	}
	printf("name: %s\n",str);
	
	ret = GetGender(text,str,sizeof(str));
	if(ret < 0) {
		goto info_error;
	}
	printf("gender: %s\n",str);

	if(type == ' ') {
		//只有身份证有此项
		ret = GetRace(text,str,sizeof(str));
		if(ret < 0) {
			goto info_error;
		}
		printf("race: %s\n",str);
	}

	ret = GetBirthday(text,str,sizeof(str));
	if(ret < 0) {
		goto info_error;
	}
	printf("birthday: %s\n",str);

	if(type != 'I' && type != 'Y') {
		//外国人永久居留证没有此项
		ret = GetAddress(text,str,sizeof(str));
		if(ret < 0) {
			goto info_error;
		}
		printf("address: %s\n",str);
	}

	ret = GetIDNum(text,str,sizeof(str));
	if(ret < 0) {
		goto info_error;
	}
	printf("IDNum: %s\n",str);

	ret = GetIssuer(text,str,sizeof(str));
	if(ret < 0) {
		goto info_error;
	}
	printf("issuer: %s\n",str);

	ret = GetValidFrom(text,str,sizeof(str));
	if(ret < 0) {
		goto info_error;
	}
	ret = GetExpireDate(text,str2,sizeof(str2));
	if(ret < 0) {
		goto info_error;
	}
	printf("valid: %s-%s\n",str,str2);

	if(type == 'J') {
		ret = GetPassNum(text,str,sizeof(str));
		if(ret < 0) {
			goto info_error;
		}
		printf("passNum: %s\n",str);

		ret = GetIssueCount(text,str,sizeof(str));
		if(ret < 0) {
			goto info_error;
		}
		printf("IssueCount: %s\n",str);
	}
	else if(type == 'I' || type == 'Y') {
		ret = GetNationCode(text,str,sizeof(str));
		if(ret < 0) {
			goto info_error;
		}
		printf("nation: %s\n",str);

		ret = GetCnName(text,str,sizeof(str));
		if(ret < 0) {
			goto info_error;
		}
		printf("cnName: %s\n",str);

		if(type == 'I') {
			//2017版外国人永久居留证
			ret = GetCardVersion(text,str,sizeof(str));
			if(ret < 0) {
				goto info_error;
			}
			printf("version: %s\n",str);
		}
		else {
			ret = GetRelationalNum(text,str,sizeof(str));
			if(ret < 0) {
				goto info_error;
			}
			printf("relational number: %s\n",str);

			ret = GetRenewalCount(text,str,sizeof(str));
			if(ret < 0) {
				goto info_error;
			}
			printf("renewal count: %s\n",str);
		}
	}

	printf("\n");

info_error:

	return ;
}

unsigned int get_sector_num(unsigned int block_num)
{
    int sector;
    if (block_num < 128)
    {
        sector = block_num / 4;
    }
    else
    {
        sector = ((block_num - 128) / 16) + 32;
    }

    return sector;
}

#define MAX_UID_FIND	6

//读取银行卡卡号等信息
int ReadBankCard(int iPort, ISO14443A_UID_T * card)
{
	int ret;
	char number[256] = {0};
	char info[sizeof(number)] = {0};
	
	ret = ReadPbocCardNumA(iPort,card->UID,card->UIDLen,number,info,sizeof(number));

	if(ret>0) {
		printf("Bank Card Number:\t%s\n",number);
		printf("Other Information:\t%s\n",info);
		return ret;
	}
	else {
		return -1;
	}
}



//读取 Mifare IC卡
int ReadMifare(int iPort, ISO14443A_UID_T * card)
{
	int ret;
	unsigned int j,i;
	int max_block,sector;
	int type = 0x60; // 0x60 为密钥A 认证, 0x61 为密钥B认证
	unsigned char data[256][16] = {0};
	unsigned char key[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
	
	//read card 
	
    if(card->SAK == 0x18)
    {
        max_block = 256;
    }
	else {
		max_block = 64;
	}

	for (i = 0, sector=-1 ;i< max_block;i++)
	{
		if(get_sector_num(i) != sector )
		{//扇区发生变化,需要重新认证
			//需要先HALT一下卡片,必要的,但暂不清楚原因
			ISO14443A_Halt(iPort, 0);
            sector = get_sector_num(i);
            ret = MIFARE1_AuthBlock(iPort, card->UID, card->UIDLen, i, type, key, 0);
            if (ret != 0x90){
				printf("Mifare Auth block %d Failed!\n",i);
                return -1;
            }
        }

        ret = MIFARE1_ReadBlock(iPort, i, data[i], 0);
        if (ret != 0x90) {
            printf("Read block %d failed!\n",i);
			return -1;
        }
    }

	/* //write block
	memset(data,rand(),16);

	
	ISO14443A_Halt(iPort, 0);
	MIFARE1_AuthBlock(iPort, uid[0].UID, uid[0].UIDLen, 1, type, key, 0);
	ret = MIFARE1_WriteBlock(iPort,1,data,0);
	if(ret != 0x90) {
		printf("Write block 1 failed!\n");
		return -1;
	}
	*/

	//

	for (i = 0;i< max_block && i<8;i++) //只显示一部分数据,否则内容太多刷屏了
	{
		for(j=0;j<16;j++) {
			if(j==8) {
				printf(" ");
			}
			printf("%02X ",data[i][j]);
		}
		printf("\n");
    }
	return max_block;
}

static unsigned char passport_data[128*1024];
static unsigned char passport_photo[256*1024];


void ReadPassport(int iPort,const char * pass_num, const char * birthday, const char * expire)
{
	int ret;
	FILE * fp;

	//读护照
	char auth[90] = {0};
	char MRZ[256];

	//生成认证码 auth
	GetICAO9303AuthCode(pass_num,birthday,expire,auth);

	//读取护照
	ret = ReadICAO9303All(iPort,auth,passport_data,sizeof(passport_data));
	
	if(ret<=0) {
		return ;
	}

	ret = GetICAO9303MRZ(passport_data,MRZ,sizeof(MRZ));
	if(ret<0) {
		return ;
	}
	//读取护照/港澳通行证成功
	printf("MRZ: %s\n",MRZ);


	ret = GetICAO9303JpgData(passport_data,passport_photo,sizeof(passport_photo));
	if(ret>0) {
		fp = fopen("image.jpg","wb+");
		if(fp) {
			fwrite(passport_photo,1,ret,fp);
			fclose(fp);
		}
	}
}

void ReadOtherCard(int iPort)
{
	int ret;
	unsigned int j,i,cnt = 0;
	ISO14443A_UID_T uid[MAX_UID_FIND];

	//find card
	ret = ISO14443A_FindCard(iPort,uid,MAX_UID_FIND,&cnt,0);
	if(ret != 0x90) {
		goto finished;
	}
	
	if(cnt <= 0) {
		goto finished;
	}

	for(i=0;i<cnt;i++) {	
		if(uid[i].SAK == 0x8) {
			printf("Mifare S50:\t");
		}
		else if(uid[i].SAK == 0x18) {
			printf("Mifare S70:\t");
		}
		else {
			printf("Unknow:\t");
		}
		for(j=0;j<uid[i].UIDLen;j++) {
			printf("%02X",uid[i].UID[j]);
		}
		printf("\n");
	}

	if(uid[0].SAK & 0x8) {
		//尝试作为 Mifare IC卡读取
		ret = ReadMifare(iPort,&uid[0]);
		if(ret>=0) {
			ISO14443A_Halt(iPort,0);
			SDT_ReaderBeep(iPort,100,0);
			Sleep(1000);
		}

		ISO14443A_ModeExit(iPort,0); //退出模式(短暂关闭天线),并Sleep一段时间,解决部分银行卡作为Mifare卡读过之后,就不能读取银行卡号的问题
		Sleep(200);
	}

	if(uid[0].SAK & 0x20) {

		//尝试作为银行卡读取
		ret = ReadBankCard(iPort,&uid[0]);
		if(ret>=0) {
			SDT_ReaderBeep(iPort,100,0);
			Sleep(1000);
			goto finished;
		}
	}

	if(uid[0].SAK & 0x20) {
		//尝试作为护照/港澳通行证读取
		ISO14443A_ModeExit(iPort,0);//  接口要求需要先退出type a模式
		Sleep(10);
		// 读取护照的参数需要通过OCR文字识别,获得护照(或港澳通行证)的号码,生日,过期时间, 并作为参数传入
		// 所以此处传入的 "C06765690", "131116", "191125" 只是示例, 需要根据实际情况修改
		ReadPassport(iPort,"C06765690","131116","191125");
	}

finished:
	// 从 ISO14443A 模式切回 ISO14443B模式(读取身份证模式),这样可以继续读取身份证,还起到了重置 ISO14443A 类型卡片的作用
	ISO14443A_ModeExit(iPort,0);
	Sleep(50);
}


int main(int argc, char * argv[])
{
	int port = 1001;
	int ret;
	unsigned char ManaInfo[4];
	unsigned char ManaMsg[8];
	unsigned char text[256];
	unsigned char image[1024];
	unsigned char finger[1024];
	unsigned char AppInfo[1024];
	unsigned char bmp[65536];
	unsigned int textLen,imageLen,fingerLen,AppInfoLen;
	unsigned int ID[4];
	char str[1024];
	FILE * fp;

	if(argc > 1) {
		port = atoi(argv[1]);
	}
#ifndef WIN32
	print_com_list();
#endif

	while(1) {

		ret = SDT_GetSAMStatus(port,1);
		if(ret != 0x90 ) {
			Sleep(200);
			continue;
		}
/*
		SDT.ResetSAM(port,1);
		Sleep(1000);
		continue;
*/

		
		ret = SDT_OpenPort(port);
		if(ret != 0x90 ) {
			Sleep(200);
			continue;
		}

		printf("reader detected!\n");

		ret = SDT_GetSAMID(port,(unsigned char *)ID,0);
		if(ret!= 0x90) {
			SDT_ClosePort(port);
			continue;
		}
		

		ret = SDT_GetSAMID(port,(unsigned char *)ID,0);
		if(ret!= 0x90) {
			SDT_ClosePort(port);
			continue;
		}

		printf("SAMID: %02d.%02d-%08d-%010d-%010d\n",ID[0]&0xffff,ID[0]>>16,ID[1],ID[2],ID[3]);

		ret = SDT_GetSAMIDToStr(port,str,0);
		if(ret!= 0x90) {
			SDT_ClosePort(port);
			continue;
		}

		printf("SAMID: %s\n",str);


		while(1) {


			ret = SDT_ReadBaseFPMsg(port,text,&textLen,image,&imageLen,finger,&fingerLen,0);
			if(ret == 0x90) {
				printf("read card success!(%d,%d,%d)\n",textLen,imageLen,fingerLen);
				goto success;
			}
			else if(ret < 0x21 ){
				break;
			}

			ret = SDT_StartFindIDCard(port,ManaInfo,0);
			if(ret == 0x9f) {
				printf("find card success!\n");
			}
			else  if(ret < 0x21 ){
				break;
			}
			else {
				//读取其他类型的卡片
				ReadOtherCard(port);
				Sleep(100);
				continue;
			}
			
			
			ret = SDT_SelectIDCard(port,ManaMsg,0);
			if(ret == 0x90) {
				printf("select card success!\n");
			}
			else  if(ret < 0x21 ){
				break;
			}
			else {
				printf("select failed!(ret=0x%02X)\n",ret);
				Sleep(1000);
				continue;
			}

			ret = SDT_ReadBaseFPMsg(port,text,&textLen,image,&imageLen,finger,&fingerLen,0);
			if(ret == 0x90) {
				printf("read card success!(%d,%d,%d)\n",textLen,imageLen,fingerLen);
				goto success;
			}
			else  if(ret < 0x21 ){
				break;
			}
			else  {
				printf("read card failed!(ret=0x%02X)\n",ret);
				// 读取护照的参数需要通过OCR文字识别,获得护照(或港澳通行证)的号码,生日,过期时间, 并作为参数传入
				// 所以此处传入的 "E18314949", "870623", "240615" 只是示例, 需要根据实际情况修改
				ReadPassport(port,"E18314949","870623","240615");//身份证读取失败了,尝试读取护照/港澳通行证

				Sleep(1000);
				continue;
			}

			

success:
			AppInfoLen = 0;
			ret = SDT_ReadNewAppMsg(port,AppInfo,&AppInfoLen,0);
			if(ret == 0x90){
				printf("read address success!(%d)\n",AppInfoLen);
			}
			else {
				printf("no address!(0x%x)\n",ret);
			}

			ret = SDT_ReadBaseFPMsgToFile(port,"text.txt",&textLen,"image.wlt",&imageLen,"finger.bin",&fingerLen,0);
			
			print_info2((char*)text);

			ret = GetBmpData(image,bmp,sizeof(bmp));
			if(ret > 0 ) {
				fp = fopen("image.bmp","wb+");
				if(fp) {
					fwrite(bmp,1,ret,fp);
					fclose(fp);
				}
			}

			Sleep(1000);
		}
		printf("reader removed!\n");
		
		SDT_ClosePort(port);
		
	}

	return 0;
}

八、Delphi

Delphi 复制代码
object Form1: TForm1
  Left = 212
  Top = 147
  Width = 1740
  Height = 900
  Caption = #27493#32852#31185#25216' '#36523#20221#35777#38405#35835#28436#31034#31243#24207
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = OnCreate
  PixelsPerInch = 120
  TextHeight = 16
  object Label1: TLabel
    Left = 16
    Top = 32
    Width = 48
    Height = 16
    Caption = #31471#21475#21495#65306
  end
  object Label2: TLabel
    Left = 16
    Top = 136
    Width = 36
    Height = 16
    Caption = #22995#21517#65306
  end
  object Label3: TLabel
    Left = 16
    Top = 176
    Width = 36
    Height = 16
    Caption = #24615#21035#65306
  end
  object Label4: TLabel
    Left = 184
    Top = 176
    Width = 36
    Height = 16
    Caption = #27665#26063#65306
  end
  object Label5: TLabel
    Left = 16
    Top = 208
    Width = 60
    Height = 16
    Caption = #20986#29983#26085#26399#65306
  end
  object Label6: TLabel
    Left = 16
    Top = 240
    Width = 36
    Height = 16
    Caption = #20303#22336#65306
  end
  object Label7: TLabel
    Left = 16
    Top = 328
    Width = 72
    Height = 16
    Caption = #36523#20221#35777#21495#30721#65306
  end
  object Label8: TLabel
    Left = 16
    Top = 368
    Width = 60
    Height = 16
    Caption = #31614#21457#26426#20851#65306
  end
  object Label9: TLabel
    Left = 16
    Top = 400
    Width = 60
    Height = 16
    Caption = #26377#25928#26399#38480#65306
  end
  object Label10: TLabel
    Left = 16
    Top = 72
    Width = 54
    Height = 16
    Caption = 'SAMID'#65306
  end
  object Label11: TLabel
    Left = 16
    Top = 104
    Width = 60
    Height = 16
    Caption = #21345#29255#24207#21495#65306
  end
  object Image1: TImage
    Left = 296
    Top = 104
    Width = 105
    Height = 105
  end
  object txtName: TEdit
    Left = 136
    Top = 136
    Width = 137
    Height = 24
    ReadOnly = True
    TabOrder = 0
  end
  object txtGender: TEdit
    Left = 136
    Top = 168
    Width = 41
    Height = 24
    ReadOnly = True
    TabOrder = 1
  end
  object btRead: TButton
    Left = 392
    Top = 32
    Width = 75
    Height = 25
    Caption = #35835#21345
    TabOrder = 2
    OnClick = btReadClick
  end
  object btOpen: TButton
    Left = 280
    Top = 32
    Width = 97
    Height = 25
    Caption = #25171#24320#31471#21475
    TabOrder = 3
    OnClick = btOpenClick
  end
  object cbPort: TComboBox
    Left = 136
    Top = 32
    Width = 129
    Height = 24
    ItemHeight = 16
    TabOrder = 4
    Text = '0'
  end
  object txtBirthday: TEdit
    Left = 136
    Top = 200
    Width = 137
    Height = 24
    ReadOnly = True
    TabOrder = 5
  end
  object txtAddress: TEdit
    Left = 136
    Top = 232
    Width = 209
    Height = 24
    ReadOnly = True
    TabOrder = 6
  end
  object txtNumber: TEdit
    Left = 136
    Top = 328
    Width = 209
    Height = 24
    ReadOnly = True
    TabOrder = 7
  end
  object txtIssuer: TEdit
    Left = 136
    Top = 360
    Width = 209
    Height = 24
    ReadOnly = True
    TabOrder = 8
  end
  object txtValid: TEdit
    Left = 136
    Top = 392
    Width = 209
    Height = 24
    ReadOnly = True
    TabOrder = 9
  end
  object txtSAMID: TEdit
    Left = 136
    Top = 64
    Width = 241
    Height = 24
    ReadOnly = True
    TabOrder = 10
  end
  object StatusBar1: TStatusBar
    Left = 0
    Top = 834
    Width = 1722
    Height = 19
    Panels = <
      item
        Width = 250
      end
      item
        Width = 450
      end>
  end
  object txtRace: TEdit
    Left = 232
    Top = 168
    Width = 41
    Height = 24
    ReadOnly = True
    TabOrder = 12
  end
  object txtCardSn: TEdit
    Left = 136
    Top = 104
    Width = 137
    Height = 24
    ReadOnly = True
    TabOrder = 13
  end
end

九、阿雪技术观

拥抱开源与共享,见证科技进步奇迹,畅享人类幸福时光!

让我们积极投身于技术共享的浪潮中,不仅仅是作为受益者,更要成为贡献者。无论是分享自己的代码、撰写技术博客,还是参与开源项目的维护和改进,每一个小小的举动都可能成为推动技术进步的巨大力量

相关推荐
starsongda11 小时前
VR科技展厅重塑科技展示新风貌,引领未来展示潮流
科技·3d·vr
河南查新信息技术研究院12 小时前
科技查新在医药健康领域的应用
大数据·科技·全文检索
lieban12 小时前
猎板PCB2到10层数的科技进阶与应用解析
科技
hostpai12 小时前
FebHost:科技公司选择.TECH域名的魅力
人工智能·科技·搜索引擎·国外域名·科技域名·.tech域名
环球生活说12 小时前
泰康以85件年金专利申请位列“中国年金科技专利排行榜”第一
科技
GZ8manage12 小时前
高亚科技签约酸动力,助力研发管理数字化升级
科技
北京搜维尔科技有限公司17 小时前
搜维尔科技:【煤矿虚拟仿真】煤矿企业、高校、科研单位-多语言支持、数字孪生、交互式学习体验
科技·机器人·vr
hostpai17 小时前
FebHost:.COM域名对于初创科技公司的优势
网络·科技·搜索引擎·网站·域名注册
山东布谷科技官方17 小时前
山东布谷科技:关于直播源码|语音源码|一对一直播源码提交App Store的流程及重构建议
科技·交友·一对一直播源码·直播app源码·语音源码
华东同舟求职17 小时前
舜宇光学科技入职测评:北森商业推理40分钟28题真题解析、网盘资料下载、答题技巧
经验分享·科技·职场和发展·求职招聘