Development of ID Card Reader with JNA

JNA (Java Native Access) is an open source Java framework based on JNI.  Project URL:
     
https://github.com/twall/jna  It is more troublesome to use JNI to call dll file writing ), users need to have a better understanding of C/C++.
      

    Using JNA, you don't need  to write a suitable dll, just  write a  Java  interface and some code, as the proxy of the dll, you can call the dll in the Java program, and JNA automatically realizes the data type mapping between Java and C.

Ready to work

     Download the JAR package . The current version of JNA is 3.5.1, which contains two jar packages: jna.jar and platform.jar. Generally, only jna.jar is needed, and platform.jar provides many tool classes.   

     The JNA runtime will look for the dll library in the path specified by the path environment variable. The user can also set the java system parameter -Djna.library.path=...

 

A simple example from the JNA official website

     Use the printf function in the C runtime library msvcrt.dll to print characters,

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/** Simple example of JNA interface mapping and usage. */
public class HelloWorld {

    // This is the standard, stable way of mapping, which supports extensive
    // customization and mapping of Java to native types.
    public interface CLibrary extends Library {

CLibrary INSTANCE = (CLibrary)Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);

void printf(String format, Object... args);

    }

   public static void main(String[] args) {
     CLibrary.INSTANCE.printf("Hello, World\n");
     for (int i=0;i < args.length;i++) {
       CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
     }
   }
}

 

Develop ID card reader using JNA

     The basic class library of ID card reader used: sdtapi.dll, WltRs.dll. The following is the sample code, which lists the methods provided in sdtapi.dll and WltRs.dll (the method name can be viewed through tools such as vc dumpbin), and introduces the basic process of reading ID cards and the use of the interface.

---IDCardReader---

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.HashMap;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;

public class IDCardReader {

/**
* sdtapi接口
* 
* 声明sdtapi.dll中的导出方法
* 
* 端口类api: SDT_OpenPort,SDT_ClosePort,SDT_GetCOMBaud,SDT_SetCOMBaud
* SAM(身份证安全控制模块security account manager)类aip: SDT_GetSAMStatus,SDT_GetSAMID,SDT_GetSAMIDToStr,SDT_ResetSAM
* 身份证卡类api: SDT_StartFindIDCard,SDT_SelectIDCard,SDT_ReadBaseMsg,SDT_ReadBaseMsgToFile,SDT_ReadNewAppMsg
* 
* 在普通开发中仅需使用部分端口类、身份证卡类api即可满足需要
* 
* @author sunjc
*
*/
public interface Sdtapi extends Library {
Sdtapi INSTANCE = (Sdtapi) Native.loadLibrary("sdtapi", Sdtapi.class);

/************************端口类API *************************/

/**
* 打开串口/USB口
* 
* @param iPort 端口号
* 1-16(十进制)为串口,1001-1016(十进制)为USB口
* @return
*/
int SDT_OpenPort(int iPort);

/**
* 关闭串口/USB口
* 
* @param iPort 端口号
* @return
*/
int SDT_ClosePort(int iPort);

/**
* 查看串口波特率
* 
* @param iPort 串口号 (1-16)
* @param pucBaudRate 串口波特率
* @return
*/
int SDT_GetCOMBaud(int iPort, IntByReference pucBaudRate);

/**
* 设置串口波特率
* 
* @param iPort 串口号 (1-16)
* @param puiCurrBaud 当前串口波特率
* @param puiSetBaud 要设置的串口波特率
* 波特率只能为以下项115200,57600,38400,19200,9600
* @return
*/
int SDT_SetCOMBaud(int iPort, int puiCurrBaud, int puiSetBaud);

/************************SAM类API *************************/

/**
* 对SAM_V 进行状态检测
* 
* @param iPort 端口号
* @param iIfOpen
* @return
*/
int SDT_GetSAMStatus(int iPort, int iIfOpen);

/**
* 读取SAM_V 的编号(十六进制)
* 
* @param iPort 端口号
* @param pucSAMID SAM_V编号
* @param iIfOpen
* @return
*/
int SDT_GetSAMID(int iPort, CharBuffer pucSAMID, int iIfOpen);

/**
* 读取SAM_V 的编号(字符串格式)
* 
* @param iPort 端口号
* @param pucSAMID SAM_V编号
* @param iIfOpen
* @return
*/
int SDT_GetSAMIDToStr(int iPort, CharBuffer pucSAMID, int iIfOpen);

/**
* 对SAM_V 复位
* 
* @param iPort 端口号
* @param iIfOpen
* @return
*/
int SDT_ResetSAM(int iPort, int iIfOpen);

/************************身份证卡类API *************************/

/**
* 找卡
* 
* @param iPort 端口号
* @param pucManaInfo 证/卡芯片治理号(4字节)
* @param iIfOpen
* 0表示不在该函数内部打开和关闭串口,此时确保之前调用了SDT_OpenPort来打开端口,并且在不需要与端口通信时,调用SDT_ClosePort关闭端口;
* 非0表示在API函数内部包含了打开端口和关闭端口函数,之前不需要调用SDT_OpenPort,也不用再调用SDT_ClosePort
* @return
*/
int SDT_StartFindIDCard(int iPort, IntByReference pucManaInfo, int iIfOpen);

/**
* 选卡
* 
* @param iPort 端口号
* @param pucManaMsg 证/卡芯片序列号(8字节)
* @param iIfOpen
* @return
*/
int SDT_SelectIDCard(int iPort, IntByReference pucManaMsg, int iIfOpen);

/**
* 读取固定信息
* 
* @param iPort 端口号
* @param pucCHMsg 文字信息
* @param puiCHMsgLen
* @param pucPHMsg 照片信息
* @param puiPHMsgLen
* @param iIfOpen
* @return
*/
int SDT_ReadBaseMsg(int iPort, CharBuffer pucCHMsg,
IntByReference puiCHMsgLen, ByteBuffer pucPHMsg,
IntByReference puiPHMsgLen, int iIfOpen);

/**
* 读取固定信息,将信息写到文件
* 
* @param iPort 端口号
* @param chMsgFile 文本信息文件名
* @param puiCHMsgLen
* @param wltFile 照片信息文件名(扩展名为wlt)
* @param puiPHMsgLen
* @param iIfOpen
* @return
*/
int SDT_ReadBaseMsgToFile(int iPort, String chMsgFile,
IntByReference puiCHMsgLen, String wltFile,
IntByReference puiPHMsgLen, int iIfOpen);

/**
* 读取追加信息
* 
* @param iPort 端口号
* @param pucAppMsg 追加信息
* @param puiAppMsgLen
* @param iIfOpen
* @return
*/
int SDT_ReadNewAppMsg(int iPort, CharBuffer pucAppMsg,
IntByReference puiAppMsgLen, int iIfOpen);

/**
* 设置射频适配器最大通信字节数
* 
* @param iPort
* @param ucByte
* @param iIfOpen
* @return
*/
int SDT_SetMaxRFByte(int iPort, String ucByte, int iIfOpen);
}

public interface Wltrs extends Library {
Wltrs INSTANCE = (Wltrs) Native.loadLibrary("WltRs", Wltrs.class);

/**
* 将wlt文件解码成bmp文件
* 
* @param wltFile wlt文件名
* @param intf 阅读设备通讯接口类型(1—RS-232C,2—USB)
* @return 
* 1 相片解码解码正确 
* 0 调用sdtapi.dll错误 
* -1 相片解码错误 
* -2 wlt文件后缀错误 
* -3 wlt文件打开错误 
* -4 wlt文件格式错误 
* -5 软件未授权 
* -6 设备连接错误
* 
* wlt文件的扩展名要固定为”.wlt”,如:xp.wlt,相片解码成xp.bmp
* 本方法要与sdtapi.dll关联使用,并确认通讯端口处于关闭状态;
*/
int GetBmp(String wltFile, int intf);
}

private static IDCardReader instance;

private int port;

private IDCardReader() {

}

public static IDCardReader getInstance() {
if (instance == null) {
instance = new IDCardReader();
}

return instance;
}

/**
* 以指定波特率打开串口
* @param port
* @param baudRate
* @return
*/
public boolean openPort(int port, int baudRate) {
int resultCode = -1;

// 取得串口当前波特率
IntByReference currBaudRate = new IntByReference();
resultCode = Sdtapi.INSTANCE.SDT_GetCOMBaud(port, currBaudRate);

if (resultCode == 144) {
// 设置串口波特率,如设置不成功将使用原波特率
resultCode = Sdtapi.INSTANCE.SDT_SetCOMBaud(port, currBaudRate.getValue(), baudRate);
}

// 打开串口
resultCode = Sdtapi.INSTANCE.SDT_OpenPort(port);

if (resultCode != 144) {
return false;
}

this.port = port;
return true;
}

/**
* 读卡
* @return
*/
public IDCardInfo readCard() {
int rtn = -1;

// 找卡
rtn = Sdtapi.INSTANCE.SDT_StartFindIDCard(port, new IntByReference(), 0);

if (rtn != 159) {
return null;
}

// 选卡
rtn = Sdtapi.INSTANCE.SDT_SelectIDCard(port, new IntByReference(), 0);

if (rtn != 144) {
return null;
}

// 读卡
CharBuffer cHMsg = CharBuffer.allocate(256); // 文字信息
ByteBuffer pHMsg = ByteBuffer.allocate(1024); // 照片信息
rtn = Sdtapi.INSTANCE.SDT_ReadBaseMsg(port, cHMsg, new IntByReference(), pHMsg, new IntByReference(), 0);

IDCardInfo idCardInfo = new IDCardInfo();
parseCHMsg(cHMsg.toString(), idCardInfo);
return idCardInfo;
}

/**
* 关闭端口
*/
public void closePort() {
Sdtapi.INSTANCE.SDT_ClosePort(port);
}

public static void main(String[] args) throws InterruptedException {
// 打开端口
IDCardReader idCardReader = IDCardReader.getInstance();
idCardReader.openPort(4, 115200); // 默认波特率为115200,一般不需要修改,速度最快

// 读卡
long starttime = System.currentTimeMillis();
System.out.println(idCardReader.readCard());
long endtime = System.currentTimeMillis(); 
System.out.println("time:" + (endtime - starttime));

// 关闭端口
idCardReader.closePort();
}

public static void test() {
// 取串口波特率
IntByReference pucBaudRate = new IntByReference(); 
System.out.println("GetCOMBaud:"+ Sdtapi.INSTANCE.SDT_GetCOMBaud(4, pucBaudRate));
System.out.println("pucBaudRate:" + pucBaudRate.getValue());

// 设置串口波特率
System.out.println("SetCOMBaud:" + Sdtapi.INSTANCE.SDT_SetCOMBaud(4, pucBaudRate.getValue(), 9600));

// 打开端口
System.out.println("OpenPort:" + Sdtapi.INSTANCE.SDT_OpenPort(4));

// 找卡
IntByReference pucManaInfo = new IntByReference();
System.out.println("StartFindIDCard:" + Sdtapi.INSTANCE.SDT_StartFindIDCard(4, pucManaInfo, 0));
System.out.println("pucManaInfo:" + pucManaInfo.getValue());

// 选卡
IntByReference pucManaMsg = new IntByReference();
System.out.println("SelectIDCard:" + Sdtapi.INSTANCE.SDT_SelectIDCard(4, pucManaMsg, 0));
System.out.println("pucManaMsg:" + pucManaMsg.getValue());

// 读卡
CharBuffer pucCHMsg = CharBuffer.allocate(256);
ByteBuffer pucPHMsg = ByteBuffer.allocate(1024);
IntByReference puiCHMsgLen = new IntByReference();
IntByReference puiPHMsgLen = new IntByReference();
System.out.println("ReadBaseMsg:" + Sdtapi.INSTANCE.SDT_ReadBaseMsg(4, pucCHMsg, puiCHMsgLen,
pucPHMsg, puiPHMsgLen, 0));
System.out.println("pucCHMsg:" + pucCHMsg + ",puiCHMsgLen:" + puiCHMsgLen.getValue());
System.out.println("身份证号:" + pucCHMsg.toString().substring(61,79)); 
System.out.println("pucPHMsg:" + pucPHMsg + ",puiPHMsgLen:" + puiPHMsgLen.getValue());

// 读卡并写文件
// System.out.println("ReadBaseMsgToFile:" + Sdtapi.INSTANCE.SDT_ReadBaseMsgToFile(4, "xx.txt", puiCHMsgLen,
// "xx.wlt", puiPHMsgLen, 1));

// 生成照片文件
writeFile("xx.wlt", pucPHMsg.array());
System.out.println("GetBmp:" + Wltrs.INSTANCE.GetBmp("xx.wlt", 2));

// 读取追加信息
// CharBuffer pucAppMsg = CharBuffer.allocate(40);
// IntByReference puiAppMsgLen = new IntByReference();
// System.out.println("ReadNewAppMsg:"+ Sdtapi.INSTANCE.SDT_ReadNewAppMsg(4, pucAppMsg, puiAppMsgLen, 1));
// System.out.println("pucAppMsg:" + pucAppMsg + ",puiAppMsgLen:" + puiAppMsgLen.getValue());

// 关闭端口
Sdtapi.INSTANCE.SDT_ClosePort(4);

}

private static void writeFile(String filename,byte[] content) {
File wltFile = new File(filename);
try {
FileOutputStream fo = new FileOutputStream(wltFile);
fo.write(content);
fo.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 解析文字信息
* @param chMsg
* @param idCardInfo
*/
private void parseCHMsg(String chMsg, IDCardInfo idCardInfo) {
idCardInfo.setName(chMsg.substring(0, 15).trim());
idCardInfo.setGender(genderCode.get(chMsg.substring(15, 16)));
idCardInfo.setNation(nationCode.get(chMsg.substring(16, 18)));
idCardInfo.setBirthday(chMsg.substring(18, 26));
idCardInfo.setAddress(chMsg.substring(26, 61).trim());
idCardInfo.setCardNo(chMsg.substring(61, 79).trim());
idCardInfo.setGrantDept(chMsg.substring(79, 94).trim());
idCardInfo.setUsefulLifeBegin(chMsg.substring(94, 102));
idCardInfo.setUsefulLifeEnd(chMsg.substring(102, 110));
}

private HashMap<String, String> nationCode = new HashMap<String, String>();
{
nationCode.put("01", "汉族");
nationCode.put("02", "蒙古族");
nationCode.put("03", "回族");
nationCode.put("04", "藏族");
nationCode.put("05", "维吾尔族");
nationCode.put("06", "苗族");
nationCode.put("07", "彝族");
nationCode.put("08", "壮族");
nationCode.put("09", "布依族");
nationCode.put("10", "朝鲜族");
nationCode.put("11", "满族");
nationCode.put("12", "侗族");
nationCode.put("13", "瑶族");
nationCode.put("14", "白族");
nationCode.put("15", "土家族");
nationCode.put("16", "哈尼族");
nationCode.put("17", "哈萨克族");
nationCode.put("18", "傣族");
nationCode.put("19", "黎族");
nationCode.put("20", "傈僳族");
nationCode.put("21", "佤族");
nationCode.put("22", "畲族");
nationCode.put("23", "高山族");
nationCode.put("24", "拉祜族");
nationCode.put("25", "水族");
nationCode.put("26", "东乡族");
nationCode.put("27", "纳西族");
nationCode.put("28", "景颇族");
nationCode.put("29", "柯尔克孜族");
nationCode.put("30", "土族");
nationCode.put("31", "达翰尔族");
nationCode.put("32", "仫佬族");
nationCode.put("33", "羌族");
nationCode.put("34", "布朗族");
nationCode.put("35", "撒拉族");
nationCode.put("36", "毛南族");
nationCode.put("37", "仡佬族");
nationCode.put("38", "锡伯族");
nationCode.put("39", "阿昌族");
nationCode.put("40", "普米族");
nationCode.put("41", "塔吉克族");
nationCode.put("42", "怒族");
nationCode.put("43", "乌孜别克族");
nationCode.put("44", "俄罗斯族");
nationCode.put("45", "鄂温克族");
nationCode.put("46", "德昂族");
nationCode.put("47", "保安族");
nationCode.put("48", "裕固族");
nationCode.put("49", "京族");
nationCode.put("50", "塔塔尔族");
nationCode.put("51", "独龙族");
nationCode.put("52", "鄂伦春族");
nationCode.put("53", "赫哲族");
nationCode.put("54", "门巴族");
nationCode.put("55", "珞巴族");
nationCode.put("56", "基诺族");
nationCode.put("57", "其它");
nationCode.put("98", "外国人入籍");
}

private HashMap<String, String> genderCode = new HashMap<String, String>();
{
genderCode.put("0", "未知");
genderCode.put("1", "男");
genderCode.put("2", "女");
genderCode.put("9", "未说明");
}
}

/**
* 身份证信息
* 
* @author sunjc
* 
*/
public class IDCardInfo {
private String name;
private String gender;
private String nation;
private String birthday;
private String address;
private String cardNo;
private String grantDept;
private String usefulLifeBegin;
private String usefulLifeEnd;
private String newAddres;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public String getNation() {
return nation;
}

public void setNation(String nation) {
this.nation = nation;
}

public String getBirthday() {
return birthday;
}

public void setBirthday(String birthday) {
this.birthday = birthday;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getCardNo() {
return cardNo;
}

public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}

public String getGrantDept() {
return grantDept;
}

public void setGrantDept(String grantDept) {
this.grantDept = grantDept;
}

public String getUsefulLifeBegin() {
return usefulLifeBegin;
}

public void setUsefulLifeBegin(String usefulLifeBegin) {
this.usefulLifeBegin = usefulLifeBegin;
}

public String getUsefulLifeEnd() {
return usefulLifeEnd;
}

public void setUsefulLifeEnd(String usefulLifeEnd) {
this.usefulLifeEnd = usefulLifeEnd;
}

public String getNewAddres() {
return newAddres;
}

public void setNewAddres(String newAddres) {
this.newAddres = newAddres;
}

@Override
public String toString() {
String cardInfo = name + "|" + gender + "|" + nation + "|" + birthday
+ "|" + address + "|" + cardNo + "|" + grantDept + "|"
+ usefulLifeBegin + "|" + usefulLifeEnd + "|" + newAddres;
return cardInfo;
}
}

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326250110&siteId=291194637