From 4b8460f4ce7f55a4025512ee161bf5b0290e43cb Mon Sep 17 00:00:00 2001
From: kejingfan <jingfan_ke@bjtu.edu.cn>
Date: Mon, 29 Jul 2024 11:24:26 +0800
Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=90=84=E9=A1=B5=E9=9D=A2?=
 =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E4=BB=8Eh=E6=96=87=E4=BB=B6?=
 =?UTF-8?q?=E6=94=B9=E4=B8=BAcpp=E6=96=87=E4=BB=B6=EF=BC=9B=20=E6=B7=BB?=
 =?UTF-8?q?=E5=8A=A0=E7=A1=AC=E4=BB=B6=E6=8E=A5=E5=8F=A3=E5=B9=B6=E6=B7=BB?=
 =?UTF-8?q?=E5=8A=A0=EF=BC=8CreaderAPI=E4=BD=BF=E7=94=A8=E7=A1=AC=E4=BB=B6?=
 =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=8E=B7=E5=8F=96invenory=E4=BF=A1=E6=81=AF?=
 =?UTF-8?q?=E5=92=8Cconnect=EF=BC=9B=20=E5=AE=8C=E6=88=90=E5=BC=80?=
 =?UTF-8?q?=E5=8D=A1=E5=88=9D=E5=A7=8B=E9=A1=B5=E9=9D=A2=EF=BC=8C=E8=8E=B7?=
 =?UTF-8?q?=E5=8F=96=E5=8D=A1=E5=8F=B7=E5=92=8C=E5=AD=A6=E5=8F=B7=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CardManageSystem.pro             |  18 +-
 HLog.h                           |  63 ++++
 VCD.cpp                          |  56 ++++
 VCD.h                            | 438 +++++++++++++++++++++++++++
 VCDOurs.cpp                      | 493 +++++++++++++++++++++++++++++++
 VCDOurs.h                        | 146 +++++++++
 databaseAPI.cpp                  |  64 ++++
 databaseAPI.h                    |  73 +----
 deviceAPI.cpp                    |  41 +++
 deviceAPI.h                      |  40 +--
 mainwindow.cpp                   |  12 +-
 mainwindow.ui                    |   8 +-
 newCardPage.h => newCardPage.cpp |  15 +-
 quitAppPage.h => quitAppPage.cpp |   6 -
 readerAPI.cpp                    |  46 +++
 readerAPI.h                      |  41 +--
 settingPage.h => settingPage.cpp |   8 +-
 17 files changed, 1411 insertions(+), 157 deletions(-)
 create mode 100644 HLog.h
 create mode 100644 VCD.cpp
 create mode 100644 VCD.h
 create mode 100644 VCDOurs.cpp
 create mode 100644 VCDOurs.h
 create mode 100644 databaseAPI.cpp
 create mode 100644 deviceAPI.cpp
 rename newCardPage.h => newCardPage.cpp (64%)
 rename quitAppPage.h => quitAppPage.cpp (84%)
 create mode 100644 readerAPI.cpp
 rename settingPage.h => settingPage.cpp (96%)

diff --git a/CardManageSystem.pro b/CardManageSystem.pro
index 298c23c..36f2e7b 100644
--- a/CardManageSystem.pro
+++ b/CardManageSystem.pro
@@ -9,18 +9,26 @@ CONFIG += c++17
 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
 
 SOURCES += \
+    VCD.cpp \
+    VCDOurs.cpp \
+    databaseAPI.cpp \
+    deviceAPI.cpp \
     main.cpp \
-    mainwindow.cpp
+    mainwindow.cpp \
+    newCardPage.cpp \
+    quitAppPage.cpp \
+    readerAPI.cpp \
+    settingPage.cpp
 
 HEADERS += \
     HF15693.h \
+    HLog.h \
+    VCD.h \
+    VCDOurs.h \
     databaseAPI.h \
     deviceAPI.h \
     mainwindow.h \
-    newCardPage.h \
-    quitAppPage.h \
-    readerAPI.h \
-    settingPage.h
+    readerAPI.h
 
 FORMS += \
     mainwindow.ui
diff --git a/HLog.h b/HLog.h
new file mode 100644
index 0000000..108e03f
--- /dev/null
+++ b/HLog.h
@@ -0,0 +1,63 @@
+//#pragma once
+#ifndef _H_LOG_H_
+#define _H_LOG_H_
+
+#include <stdio.h>
+typedef unsigned char uchar_t;
+
+class CHLog
+{
+public:
+	CHLog(void)
+	{
+		m_fp = fopen( "hfsim.log", "w");
+	}
+	~CHLog(void)
+	{
+		if( m_fp )
+		{
+			fclose( m_fp );
+		}
+	}
+
+public:
+	void logstr( char *str )
+	{
+		if( m_fp )
+		{
+			fprintf( m_fp, "%s\n", str );
+		}
+	}
+
+	void logDIM( int len, uchar_t aucDat[] )
+	{
+		int n = len/16;
+		int k = 0;
+
+		if( m_fp )
+		{
+			fprintf( m_fp, "hex----------------\n");
+			for( int i = 0; i < n; i++ )
+			{
+				for( int j = 0; j < 16; j++ )
+				{
+					fprintf( m_fp, "%02x ", aucDat[k++]);
+				}
+				fprintf( m_fp, "\n" );
+			}
+
+			for( int j = 0; j < len%16; j++ )
+			{
+				fprintf( m_fp, "%02x ", aucDat[k++]);
+			}
+			fprintf( m_fp, "\n" );
+			fprintf( m_fp, "hex----------------\n");
+		}
+	}
+
+protected:
+	FILE *m_fp;
+};
+
+#endif
+
diff --git a/VCD.cpp b/VCD.cpp
new file mode 100644
index 0000000..0169399
--- /dev/null
+++ b/VCD.cpp
@@ -0,0 +1,56 @@
+#include "VCD.h"
+
+#define POLYNOMIAL		0x8408		// x^16 + x^12 + x^5 + 1
+#define PRESET_VALUE	0xFFFF
+#define CHECK_VALUE		0xF0B8
+#define CALC_CRC		1
+#define CHECK_CRC		0
+
+void CVCD::CRC16( int buflen, uchar_t buf[], uchar_t *pucCRCL, uchar_t *pucCRCH )
+{
+	unsigned int current_crc_value;
+	unsigned char *array_of_databytes;
+	int number_of_databytes;
+	int i, j;
+
+	number_of_databytes = buflen;
+	array_of_databytes = buf;
+	current_crc_value = PRESET_VALUE;
+	for (i = 0; i < number_of_databytes; i++)
+	{
+		current_crc_value = current_crc_value ^ ((unsigned int)array_of_databytes[i]);
+		for (j = 0; j < 8; j++)
+		{
+			if (current_crc_value & 0x0001)
+			{
+				current_crc_value = (current_crc_value >> 1) ^ POLYNOMIAL;
+			}
+			else
+			{
+				current_crc_value = (current_crc_value >> 1);
+			}
+		}
+	}
+	current_crc_value = ~current_crc_value;
+	*pucCRCL = current_crc_value & 0xFF;
+	*pucCRCH = (current_crc_value >> 8) & 0xFF;
+}
+
+void CVCD::ISO15693_getCRC16( int buflen, uchar_t buf[], uchar_t *pucCRCL, uchar_t *pucCRCH )
+{
+	CRC16( buflen, buf, pucCRCL, pucCRCH );
+}
+
+bool CVCD::ISO15693_checkCRC16( int buflen, uchar_t buf[] )
+{
+	uchar_t ucCRCL, ucCRCH;
+	CRC16( buflen-2, buf, &ucCRCL, &ucCRCH );
+	if( ucCRCL == buf[buflen-2] && ucCRCH == buf[buflen-1] )
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
diff --git a/VCD.h b/VCD.h
new file mode 100644
index 0000000..8acf807
--- /dev/null
+++ b/VCD.h
@@ -0,0 +1,438 @@
+//-----------------------------------------
+// 关于类说明
+// 类 CVCD 用于 描述 VCD
+// 应用软件调用类的成员函数,可以对VICC进行访问
+// 
+// 派生类:CVCD_Sim 用软件仿真VCD,通过transponderEnter函数仿真VICC进入了射频场
+// class CVCD_Sim : public CVCD
+// 
+// 派生类:CVCD_Ours 封装了Ours的reader.
+// class CVCD_Ours : public CVCD
+// 
+// 
+//#pragma once
+
+#ifndef _VCD_H_
+#define _VCD_H_
+
+#include "HLog.h"
+#include <cstring>
+
+// 
+// 并部分实现函数的部分功能
+// 比如select request,寻址模式一定是:address mode
+// 
+// 有些命令,可以由用户确定寻址模式
+// 00: 所有的卡都应答
+// 01: 被选择卡应答
+// 02: 特定地址的卡应答
+// else: 不支持;
+//
+//---------------------------------------------------
+// 下面定义了reader成员函数的返回结果
+// 
+// 比如一个addressed的命令,收到多个应答,或者没有应答
+// 注意:此处没有应答,不指VICC没有影带,有些场景,VICC不应答,也是正确的
+// 比如stay quiet,收不到任何响应,其实是正确的。
+// 此处没有应答,指VICC应该应答而没有应答
+// 
+#define ISO15693_READER_OK				0xA0
+#define ISO15693_READER_ER_LESS_ANSWER	0xA1	// 没有应答
+#define ISO15693_READER_ER_MORE_ANSWERS	0xA2	// 收到多个应答
+#define ISO15693_READER_ER_FRAME		0xA3	// 收到frame格式错,可能发生了冲突/软件表示frame错误.
+
+// 副载波,0: for single subcarrier / 1: for double subcarrier
+#define ISO15693_REQ_FLAG_SUB_CARRIER_S	0 
+#define ISO15693_REQ_FLAG_SUB_CARRIER_D	1
+// 速率,  0: for low data rate / 1: for high data rate
+#define ISO15693_REQ_FLAG_DATA_RATE_H 	0
+#define ISO15693_REQ_FLAG_DATA_RATE_H 	1
+// 编码方式,0: for 1/4 / 1 for 1/256;
+#define ISO15693_CODING_1_FROM_4		0
+#define ISO15693_CODING_1_FROM_256		1
+
+#define ISO15693_RSP_ERR_NO				0
+
+#define MAX_TRANSPONDERS	16
+#define MAX_PKT_LEN			1024
+
+// 寻址模式
+// 除inventory命令外,其他都涉及到寻址模式.
+
+#define ISO15693_INVENTORY_NO_VICC		0x00
+#define ISO15693_INVENTORY_ONE_VICC		0x01
+#define ISO15693_INVENTORY_MORE_VICCS	0x02
+
+// VCD的射频场最多支持的VICC数
+// 定义为16个,是因为inventory采用16slots的情况下,最多一次可以清晰收到16个VICC的应答。
+// 如果发生了碰撞,或者访问清晰收到的,或者继续寻找碰撞的。
+#define MAX_NUM_VICCS						16
+
+typedef unsigned char uchar_t;
+
+typedef enum
+{
+	mode_all,
+	mode_select,
+	mode_addressed
+}ISO15693_MODE_E;
+
+// VICC的部分信息.
+typedef struct tag_VICCInfo
+{
+	uchar_t aucUID[8];
+	uchar_t ucValid;			// 0 for unvalid, 1 for valid.
+	uchar_t ucDSFID;
+	uchar_t ucAFI;
+	uchar_t ucBlockNum;
+	uchar_t ucBlockSize;
+	uchar_t ucICRef;
+}VICCInfo;
+
+class CVCD
+{
+public:
+	CVCD(void)
+	{
+		m_ucSubCarrier = ISO15693_REQ_FLAG_SUB_CARRIER_D;	// 默认双载波
+		m_ucDataRate = ISO15693_REQ_FLAG_DATA_RATE_H;		// 默认高速率
+		m_ucDataCoding = ISO15693_CODING_1_FROM_4;
+		m_ucSlots = 16;	
+		m_pLog = NULL;
+		for( int i = 0; i < MAX_NUM_VICCS; i++ )
+		{
+			m_astVICCs[i].ucValid = 0;
+		}
+	}
+	virtual ~CVCD(void)
+	{
+	}
+
+public:
+	// 正式执行inventory前,设置这些参数
+	int set( uchar_t ucSubCarrier, uchar_t ucDataRate, uchar_t ucSlots  )
+	{
+		m_ucSubCarrier = ucSubCarrier;
+		m_ucDataRate = ucDataRate;
+		m_ucSlots = ucSlots;
+	}
+	int setSubCarrier( uchar_t ucSubCarrier )
+	{
+		m_ucSubCarrier = ucSubCarrier;
+	}
+	int setDataRate( uchar_t ucDataRate )
+	{
+		m_ucDataRate = ucDataRate;
+	}
+	int setSlots( uchar_t ucSlots )
+	{
+		m_ucSlots = ucSlots;
+	}
+	void setDataCoding_1from4()
+	{
+		m_ucDataCoding = ISO15693_CODING_1_FROM_4;
+	}
+	void setDataCoding_1from256()
+	{
+		m_ucDataCoding = ISO15693_CODING_1_FROM_256;
+	}
+	void setLog( CHLog *plog )
+	{
+		m_pLog = plog;
+	}
+
+	// 如何设置和VCD/Reader的连接?
+	// 串口.
+
+	virtual int connectReader()
+	{
+		return 1;
+	}
+	virtual int disconnectReader( )
+	{
+		return 1;
+	}
+
+/*
+	virtual int connectReaderByCOM( int comno )
+	{
+		return 1;
+	}
+*/
+	// 在执行了inventory后,用户可以获得芯片的信息。
+	// 其中获得的blocksize和blocknum在readblocks()和write block()时,有用.
+	VICCInfo getVICCInfo( uchar_t aucUID[8] )
+	{
+		VICCInfo vicc;
+
+		memset( (uchar_t *)&vicc, 0x00, sizeof(vicc)); 
+		int k = getVICC( aucUID );
+		if( k != -1 )
+		{
+			return m_astVICCs[k];
+		}
+		return vicc;
+	}
+public:
+	// 协议命令
+	// 返回:>0表示读到的Transponder数量;
+	// 
+	virtual int inventory( 
+        bool bAFIUsed,
+        uchar_t ucAFI,
+        int nViccNum,				// 应用层可以接收的UID数量;
+        uchar_t aucDSFID[],
+        uchar_t aucUID[][8] )
+	{
+		m_bAFIUsed = bAFIUsed;
+		if( m_bAFIUsed )
+		{
+			m_ucAFI = ucAFI;
+		}
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	//------------------------------------------
+	// 状态转换
+	// stayquiet
+	// cmd=0x02;
+	// 寻址方式:只能是地址模式, UID必须;
+	virtual int stayQuiet( uchar_t *pucUID )
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	// select命令,
+	// cmd=0x25
+	// 寻址方式一定是地址模式, UID必须;
+	virtual int select( uchar_t *pucUID )
+	{
+		return ISO15693_RSP_ERR_NO;
+
+	}
+
+	// reset to ready
+	// cmd=0x26
+	// 可以使地址模式,也可以是选择模式
+	virtual int resetToReady( uchar_t *pucUID )
+	{ 
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	//------------------------------------------
+	// 操作
+	// 操作类命令,寻址模式只能:地址,或者select
+	// 
+
+	// 0x20
+	virtual int readSingleBlock( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t optional_flag,	// optional bit = 1, 要求返回block的安全状态
+		uchar_t ucBlkno,			// blockno
+		uchar_t buf[],			// 返回读取的数据
+		uchar_t *pucSecurity	// 如果optional_flag = 1, 返回该block的安全状态:1表示locked.
+	)
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	// 0x21
+	virtual int writeSingleBlock( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t ucBlkno,			// 
+		uchar_t ucBlksize,
+		uchar_t buf[] )
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	// 0x22
+	virtual int lockBlock( 
+		uchar_t *pucUID,		// NULL表示select模式
+		int blockno )
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	// 0x23
+	virtual int readMultipleBlocks(
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t optional_flag,	// optional bit = 1, 要求返回block的安全状态
+		uchar_t ucBlkno,		// 开始block no
+		uchar_t blocknum,			// 实际读的block是:blocknum+1;
+		uchar_t buf[],			// 返回读取的数据
+		uchar_t aucSecurity[]	// 如果optional_flag = 1, 返回该block的安全状态:1表示locked.
+		)
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	// 0x24
+	virtual int writeMultipleBlocks( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t ucBlkno,		// 开始block no
+		uchar_t blocknum,		// 实际写的block是:blocknum+1;
+		uchar_t ucBlksize,
+		uchar_t buf[] )
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+
+	//----------------------------------------------------------
+	// AFI & DSFID
+	// 
+	// CMD: 0x27
+	// optional 不处理.
+	virtual int writeAFI( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t ucAFI )
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+
+	// 
+	// CMD: 0x28
+	// optional 不处理.
+	virtual int lockAFI(
+		uchar_t *pucUID		// NULL表示select模式
+		)
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+
+	// 
+	// CMD: 0x29
+	// optional 不处理.
+	virtual int writeDSFID( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t ucDSFID )
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+
+	// 
+	// CMD: 0x2A
+	// optional 不处理.
+	virtual int lockDSFID(
+		uchar_t *pucUID			// NULL表示select模式
+		)
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+
+	// CMD: 0x2B
+	// 
+	virtual int getSystemInfo(
+		uchar_t *pucUID,		
+		uchar_t *pinfoFlag,
+		uchar_t *pDSFID, uchar_t *pAFI, uchar_t *pBlockNum, uchar_t *pBlockSize, uchar_t *pICRef 
+		)
+	{
+		return ISO15693_RSP_ERR_NO;
+	}
+protected:
+	void CRC16( int buflen, uchar_t buf[], uchar_t *pucCRCL, uchar_t *pucCRCH );
+	bool ISO15693_checkCRC16( int buflen, uchar_t buf[] );
+	void ISO15693_getCRC16( int buflen, uchar_t buf[], uchar_t *pucCRCL, uchar_t *pucCRCH );
+
+	/**
+	 *	在inventory后,获取某个VICC的systeminfo
+	 *	生成CVICC.
+	 *	其中:blocksize & blocknum 用于以后执行read,write命令用。
+	 *	输入:pucUID. 8字节的UID.
+	 */
+	virtual bool setVICC( uchar_t aucUID[8] )
+	{
+		uchar_t infoFlag;
+		uchar_t ucDSFID, ucAFI, ucBlockNum, ucBlockSize;
+		uchar_t ucICRef;
+
+		if( getSystemInfo( aucUID, &infoFlag, &ucDSFID, &ucAFI, &ucBlockNum, &ucBlockSize, &ucICRef ) != ISO15693_RSP_ERR_NO )
+		{
+			return false;
+		}
+
+		// OK.
+		for( int i = 0; i < MAX_NUM_VICCS; i++ )
+		{
+			if( m_astVICCs[i].ucValid == 0 )
+			{
+				m_astVICCs[i].ucValid = 1;
+				if( infoFlag & (1 << 0) )
+				{
+					m_astVICCs[i].ucDSFID = ucDSFID;
+				}
+				else if( infoFlag & ( 1 << 1 ))
+				{
+					m_astVICCs[i].ucAFI = ucAFI;
+				}
+				else if( infoFlag & ( 1 << 2 ) )
+				{
+					m_astVICCs[i].ucBlockNum = ucBlockNum;
+					m_astVICCs[i].ucBlockSize = ucBlockSize;
+				}
+				else if( infoFlag & ( 1 << 3 ))
+				{
+					m_astVICCs[i].ucICRef = ucICRef;
+				}
+				memcpy( m_astVICCs[i].aucUID, aucUID, 8 );
+				return true;
+			}
+		}
+		return false;
+	}
+	int getVICC( uchar_t aucUID[8] )
+	{
+		for( int i = 0; i < MAX_NUM_VICCS; i++ )
+		{
+			if( memcmp(m_astVICCs[i].aucUID, aucUID, 8 ) == 0 )
+			{
+				return i;
+			}
+		}
+		return -1;
+	}
+protected:
+	void log( char *str )
+	{
+		if( m_pLog )
+		{
+			m_pLog->logstr( str );
+		}
+	}
+
+	void logDIM( int len, uchar_t aucDat[] )
+	{
+		if( m_pLog )
+		{
+			m_pLog->logDIM( len, aucDat );
+		}
+	}
+
+protected:
+	// 全局性配置
+	// VCD自己的选择
+	uchar_t	m_ucDataCoding;		// 编码方式,0: for 1/4 / 1 for 1/256;
+	uchar_t m_ucSlots;			// 0:16 slots or 1 slots;
+	// VCD对VICC的要求
+	uchar_t	m_ucSubCarrier;		// 副载波,0: for single subcarrier / 1: for double subcarrier 
+	uchar_t	m_ucDataRate;		// 速率,  0: for low data rate / 1: for high data rate
+
+	// 执行防碰撞循环时,可能多次发送inventory request,每个都涉及是否使用AFI
+	// 因此设置为成员变量;
+	// 
+	uchar_t	m_ucAFI;
+	bool m_bAFIUsed;
+
+	// 记录日志.
+	CHLog*	m_pLog;
+
+	// 通过inventory命令后,发现的viccs.
+	VICCInfo m_astVICCs[MAX_NUM_VICCS];
+};
+
+#endif
diff --git a/VCDOurs.cpp b/VCDOurs.cpp
new file mode 100644
index 0000000..3f9499c
--- /dev/null
+++ b/VCDOurs.cpp
@@ -0,0 +1,493 @@
+#include <cstdlib>
+#include "VCDOurs.h"
+
+CVCDOurs::CVCDOurs(void)
+{
+	m_bComOpened = false;
+}
+
+
+CVCDOurs::~CVCDOurs(void)
+{
+	if( m_bComOpened )
+	{
+		t15portClose();
+	}
+
+}
+
+// 使用串口和建立连接
+int CVCDOurs::connectReaderByCOM( int comno )
+{
+	if(t15portOpen(comno))
+	{
+		t15bSubCarrier=m_ucSubCarrier;
+		t15bDataCoding=m_ucDataCoding;
+		t15bFullPower=1;
+		t15bDataRate=m_ucDataRate;
+
+		m_bComOpened = true;
+		if( t15setProtocol() == 0 )
+		{
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void CVCDOurs::disconnectReaderByCOM( )
+{
+	if( m_bComOpened )
+	{
+		t15portClose( );
+	}
+}
+
+int CVCDOurs::inventory( 
+        bool bAFIUsed,
+        uchar_t ucAFI,
+        int nViccNum,				// 应用层的接收的UID数量;
+        uchar_t aucDSFID[],
+        uchar_t aucUID[][8] )
+{
+	t15bInventory=1;
+
+	// t15bAddress & t15bSelect在inventory=1的情况下,表示: AFI 和 NBSlots
+	// 而NBSlots为16,取值为0.
+	t15bAddress=0;		// 本来就没有用
+	t15bOption=0;		// 没用用.
+
+	if( bAFIUsed )
+	{
+		t15bSelect=1;		// 此处的含义是没有AFI,其实可以有!
+		sprintf( t15AFI, "%02x", ucAFI );
+	}
+
+	char* strUID[16];
+	for( int i = 0; i < 16; i++ )
+	{
+		strUID[i] = new char[50];
+	}
+	int n = doInventory( strUID );
+	n = ( n >= nViccNum ) ? nViccNum : n;
+	// 开始解析.
+	for( int i = 0; i < n; i++ )
+	{
+		StringToHex( strUID[i], aucUID[i] );
+	}
+
+	for( int i = 0; i < 16; i++ )
+	{
+		delete []strUID[i];
+	}
+	return n;
+}
+
+// Ours
+// 1. 执行inventory时,因为:t15bSelect=0,因此没有AFI flag.
+// 2. 没有处理返回的DSFID.
+//
+int CVCDOurs::inventory( char *pTagUID[] )
+{
+	t15bInventory=1;
+	// t15bAddress & t15bSelect在inventory=1的情况下,表示: AFI 和 NBSlots
+	// 而NBSlots为16,取值为0.
+	t15bAddress=0;		// 本来就没有用
+	t15bSelect=0;		// 此处的含义是没有AFI,其实可以有!
+	t15bOption=0;		// 没用用.
+
+	return doInventory( pTagUID );
+}
+
+int CVCDOurs::doInventory( char *pTagUID[] )
+{
+	// 对返回的UID
+	// 每个[],一个UID
+	// []表示分配的slot数
+	// [z,40]中, 'z'表示发生了冲突
+	// 返回的序列是防冲突过程中的全部序列。
+	// 
+	char cmdsend[CMD_LEN],reply[CMD_LEN]/*="23[,40][2343334239,55][,49][abcderfdsdfs0d,30]"*/;
+		
+	t15execute(INVENTORY,cmdsend,reply);
+
+	int k = 0;
+	char *p,*q,tstr[50]={0};
+	p=reply;
+
+	while (1)
+	{
+		p = strchr(p,'[');
+
+		if (NULL == p)
+			break ;
+
+		q = strchr(p+1,',');
+
+		if (NULL == q)
+			break ;
+
+		// 'z'表示冲突
+		if (q != (p+1) && *(p+1) != 'z' )
+		{
+			t15changeByteOrder(tstr,p+1,q-p-1);	//返回的数据是低字节在前,调整字节序
+			strcpy( pTagUID[k++], tstr );
+
+			// 获取某个Tag的详细信息:call GetSystemInfo().
+			setVICCStr( pTagUID[k-1] );
+		}
+
+		p = q+1;
+	}
+	return k;
+}
+
+//已经通过UID,获得了VICC的信息.
+int CVCDOurs::getSystemInfo(
+		uchar_t *pucUID,		
+		uchar_t *pinfoFlag,
+		uchar_t *pDSFID, uchar_t *pAFI, uchar_t *pBlockNum, uchar_t *pBlockSize, uchar_t *pICRef 
+		)
+{
+	char str[100];
+
+	t15bOption = 0;
+	t15bInventory = 0;
+	if( pucUID )
+	{
+		t15bAddress=1;
+		t15bSelect = 0;
+		HexToString( pucUID, 8, str );
+		t15changeByteOrder( t15UID, str, 16 );
+		t15UID[16]=0;
+	}
+	else
+	{
+		t15bAddress=0;
+		t15bSelect =1;
+	}
+
+	//执行命令
+	char cmdstr[CMD_LEN] = {0},reply[CMD_LEN] = {0};
+
+	if (!t15execute(T15SYSTEM_INFO,cmdstr,reply))
+	{	
+		return 0;
+	}
+
+	// 开始解释数据.
+	char *p;
+	char tflag[5], iflag, rflag;
+	{
+		if (! (p = strchr (reply, '[') ) )
+			return 0;
+
+		p++;
+
+		if (*p && (*p == ']' || *p == 'z' || *p == 'Z') )
+			return 0;
+
+		tflag[0]=*p++;
+		tflag[1]=*p++;
+		tflag[2]='\0';
+		rflag=(char)strtol(tflag,NULL,16);
+		tflag[0]=*p++;
+		tflag[1]=*p++;
+		tflag[2]='\0';
+
+		if( rflag != 0x00)	//Err-Flag is set
+		{
+			return rflag;
+		}
+		else
+		{
+			iflag=(char)strtol(tflag,NULL,16);
+			*pinfoFlag = iflag;
+
+			p += 16;
+			if( iflag & 0x01 )
+			{
+				tflag[0]=*p++;
+				tflag[1]=*p++;
+				tflag[2]='\0';
+				*pDSFID =(char)strtol(tflag,NULL,16);
+			}
+			if( iflag & 0x02 )
+			{
+				tflag[0]=*p++;
+				tflag[1]=*p++;
+				tflag[2]='\0';
+				*pAFI =(char)strtol(tflag,NULL,16);
+			}
+			if( iflag & 0x04 )
+			{
+				tflag[0]=*p++;
+				tflag[1]=*p++;
+				tflag[2]='\0';
+				*pBlockNum =(char)strtol(tflag,NULL,16);
+				tflag[0]=*p++;
+				tflag[1]=*p++;
+				tflag[2]='\0';
+				*pBlockSize = ((char)strtol(tflag,NULL,16))&0x1F;
+			}
+			if( iflag & 0x08 )
+			{
+				tflag[0]=*p++;
+				tflag[1]=*p++;
+				tflag[2]='\0';
+				*pICRef = ((char)strtol(tflag,NULL,16))&0x1F;
+			}
+		}
+	}
+	return rflag;
+}
+
+
+// VCD不处理一些参数的合法性判断,比如nFirstBlock等
+// 因为:1. VCD没有必要保留;2.射频场中可能存在不同的VICC,从而导致参数不同;
+//      3. 不要说VCD知道每个UID的参数,所以判断。虽然可以,但不合理,即使pucUID=NULL,即处理SELECT模式的VICC时,虽然
+//         VCD的确知道是哪个VICC。这个任务应该交给VICC,VICC会返回错误通知用户;
+// 用户可以在inventory后,使用get system info获取需要的信息;
+
+int CVCDOurs::readBlocks( int nFirstBlock, int nBlockNum, uchar_t *pDat, uchar_t *pSecurity, uchar_t *pucUID  )
+{
+// m_nBlockNum想表示 VICC 的可用block数。
+// 可以不做这种判断,毕竟,m_nBlockNum不是VCD的参数,而是VICC的属性,应该由VICC判断并返回.
+	char str[100];
+
+	t15bOption = 0;
+	t15bInventory = 0;
+	if( pucUID )
+	{
+		t15bSelect = 0;
+		t15bAddress=1;
+		HexToString( pucUID, 8, str );
+		t15changeByteOrder( t15UID, str, 16 );
+		t15UID[16]=0;
+	}
+	else
+	{
+		t15bSelect = 1;
+		t15bAddress =0;
+	}
+
+	sprintf( t15FirstBN, "%02d", nFirstBlock );
+	sprintf( t15NumBl, "%02d", nBlockNum - 1 );
+
+	//执行命令
+	char cmdstr[CMD_LEN] = {0},reply[CMD_LEN] = {0};
+	
+	int cmdno = ( nBlockNum == 1 )?READ_SB:READ_MB;
+	if (!t15execute(cmdno,cmdstr,reply))
+	{	
+		return false;
+	}
+
+	//处理返回数据
+	char *p,*q,tstr[CMD_LEN];
+	p=strchr(reply,'[')+1;
+
+	if(NULL == p)
+		return 0;
+
+	q=strchr(p,']');
+
+	if(NULL == q) 
+		return 0;
+
+	*q=0;
+	
+//	if('z' == *p ) 
+	// 2023/7/4,修改:
+	// 返回的reply,出现过如下的值: “[]”
+	// 此时*q=0,导致']'被0替换;
+	// 后续代码 tf[0/1] = *p++,导致 p > q;
+	// 再调用t15changeByteOrder()时,q-p = -2 < 0;
+	// 而:t15changeByteOrder()函数定义如下:
+/*
+HF15693_API void  t15changeByteOrder(char *pout, const char *pin,int lenbytes)
+{
+    int i;
+    for (i = 0; i < lenbytes; i += 2)
+    {
+        pout[lenbytes-2-i] = pin[i];
+        pout[lenbytes-1-i] = pin[i+1];
+    }
+    pout[lenbytes] = 0;
+}
+*/
+	// 在 lenbytes =-2 < 0的情况下,直接访问了pout[-2],导致程序直接崩溃!
+
+	// 修改如下:
+	if('z' == *p || p == q ) 
+	{
+		return 0;
+	}
+	// 同时放开该判断!
+	if(2 < q-p)
+	{
+		char tf[3];
+		tf[0]=*p++;
+		tf[1]=*p++;
+		tf[2]=0;
+		int tfi=strtoul(tf,NULL,10);
+
+		// 因为代码:*q = 0,导致tf是空串;
+		// 因此tfi=0, 所以下述语句起不到判断作用;
+		if(OP_RESPONSE_FLAG_ERROR & tfi)	//检查标志位,是否发生错误
+			return 0;
+
+		if(READ_SB == cmdno)
+		{
+			t15changeByteOrder(tstr,p,q-p);
+		}
+		else
+		{
+			int i=strtoul(t15NumBl,NULL,10)+1;
+			t15changeByteOrder_MB(tstr,p,i);
+		}
+
+		StringToHex( tstr, pDat );
+		return strlen( tstr )/2;
+	}
+	return 0;
+}
+
+int CVCDOurs::getValueFromChar( char c )
+{
+	if( c >= '0' && c <= '9' )
+	{
+		return c-'0';
+	}
+	else if( c >= 'a' && c <= 'f' )
+	{
+		return c-'a' + 10;
+	}
+	else if( c >= 'A' && c <= 'F' )
+	{
+		return c - 'A' + 10;
+	}
+	else
+	{
+		return -1;
+	}
+}
+// 返回UID的字节长度
+// 如果不是偶数,把str最后一个字符丢弃
+int CVCDOurs::StringToHex( char *str, uchar_t aucUID[] )
+{
+	int k = 0;
+	int t1, t2;
+
+	int len = strlen( str )/2;
+	for( int i = 0; i < len; i++ )
+	{
+		t1 = getValueFromChar(str[k++]);
+		t2 = getValueFromChar(str[k++]);
+		aucUID[i] = ( t1 << 4 ) + t2;
+	}
+
+	return len;
+}
+void CVCDOurs::HexToString( uchar_t aucDat[], int len, char *str )
+{
+	int k = 0;
+	int t;
+	for( int i = 0; i < len; i++ )
+	{
+		t = aucDat[i] >> 4;
+		if( t <= 9 )
+		{
+			str[k++] = t + '0';
+		}
+		else
+		{
+			str[k++] = t - 10 + 'A';
+		}
+
+		t = aucDat[i] & 0x0F;
+		if( t <= 9 )
+		{
+			str[k++] = t + '0';
+		}
+		else
+		{
+			str[k++] = t - 10 + 'A';
+		}
+	}
+	str[k] = '\0';
+}
+int CVCDOurs::writeBlocks( int nFirstBlock, int nBlockNum, uchar_t *pDat, uchar_t *pucUID )
+{
+	int k = 0;
+	for( int i = 0; i < nBlockNum; i++ )
+	{
+		k += writeBlock( nFirstBlock + i, pDat+i*8, pucUID );
+	}
+	return k;
+}
+int CVCDOurs::writeBlock( int nFirstBlock, uchar_t *pDat, uchar_t *pucUID )
+{
+	char str[100];
+
+	t15bOption = 0;
+	t15bInventory = 0;
+	t15bSelect = 0;
+	if( pucUID )
+	{
+		t15bAddress = 1;
+		HexToString( pucUID, 8, str );
+		t15changeByteOrder( t15UID, str, 16 );
+		t15UID[16]=0;
+	}
+	else
+	{
+		t15bAddress = 0;
+	}
+
+	sprintf( t15FirstBN, "%02d", nFirstBlock );
+	sprintf( t15NumBl, "00" );
+
+	// 此处4是blocksize.
+	HexToString( pDat, 4, str );
+	t15changeByteOrder( t15Data, str, 8 );
+	t15Data[8]=0;
+	strcpy( str, t15Data );
+
+	char cmdstr[CMD_LEN] = {0}, reply[CMD_LEN] = {0};
+
+	if (!t15execute(WRITE_SB,cmdstr,reply))
+    {
+//		MessageBox("执行错误,可能参数错误", 0, MB_ICONSTOP);
+		return 0 ;
+	}
+
+	char *p,*e;
+
+	if (! (p = strchr (reply, '[') ) )
+		return 0;
+
+	p++;
+
+	//if (*p && (*p == ']' || *p == 'z' || *p == 'Z') )	return 2;
+	if (! (e = strchr(p, ']')))
+		return 0;
+
+	if (e == p)
+		return 0;
+
+//	if( 2 <= e-p)
+	{
+		char tf[3];
+		tf[0] = *p++;
+		tf[1] = *p++;
+		tf[2] = '\0';
+
+		char tfi=(char)strtol(tf,NULL,16);//
+
+		if (OP_RESPONSE_FLAG_ERROR & tfi)
+			return 0;
+		else
+			return 1;
+	}
+}
diff --git a/VCDOurs.h b/VCDOurs.h
new file mode 100644
index 0000000..4e3a32c
--- /dev/null
+++ b/VCDOurs.h
@@ -0,0 +1,146 @@
+//#pragma once
+
+#ifndef _VCD_OURS_H_
+#define _VCD_OURS_H_
+
+#include "VCD.h"
+
+#include "HF15693.h"
+
+class CVCDOurs :
+	public CVCD
+{
+public:
+	CVCDOurs(void);
+	~CVCDOurs(void);
+
+public:
+	virtual int connectReaderByCOM( int comno );
+	virtual void disconnectReaderByCOM( );
+public:
+	int inventory( char *pTagUID[] );
+
+	// 执行inventory!
+	// ours reader: 支持AFI
+	// 但:不返回DSFID.
+	// 
+	virtual int inventory( 
+        bool bAFIUsed,
+        uchar_t ucAFI,
+        int nViccNum,				// 应用层的接收的UID数量;
+        uchar_t aucDSFID[],
+        uchar_t aucUID[][8] );
+
+	// 通过该命令,或者VICC信息
+	// 
+	virtual int getSystemInfo(
+		uchar_t *pucUID,		
+		uchar_t *pinfoFlag,
+		uchar_t *pDSFID, uchar_t *pAFI, uchar_t *pBlockNum, uchar_t *pBlockSize, uchar_t *pICRef 
+		);
+	//------------------------------------------
+	// 操作
+	// 操作类命令,寻址模式只能:地址,或者select
+	// 
+
+	// 0x20
+	virtual int readSingleBlock( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t optional_flag,	// optional bit = 1, 要求返回block的安全状态
+		uchar_t ucBlkno,		// blockno
+		uchar_t buf[],			// 返回读取的数据
+		uchar_t *pucSecurity	// 如果optional_flag = 1, 返回该block的安全状态:1表示locked.
+	)
+	{
+		// 没有处理optional-flag
+		// 
+		t15bOption = optional_flag;
+		return readBlocks( ucBlkno, 1, buf, pucSecurity, pucUID );
+	}
+
+	// 0x21
+	virtual int writeSingleBlock( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t ucBlkno,			// 
+		uchar_t ucBlksize,
+		uchar_t buf[] )
+	{
+		return writeBlocks( ucBlkno, 1, buf, pucUID );
+	}
+	// 0x23
+	virtual int readMultipleBlocks(
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t optional_flag,	// optional bit = 1, 要求返回block的安全状态
+		uchar_t ucBlkno,		// 开始block no
+		uchar_t blocknum,		// 实际读的block是:blocknum+1;
+		uchar_t buf[],			// 返回读取的数据
+		uchar_t aucSecurity[]	// 如果optional_flag = 1, 返回该block的安全状态:1表示locked.
+		)
+	{
+		t15bOption = optional_flag;
+		return readBlocks( ucBlkno, blocknum+1, buf, aucSecurity, pucUID );
+	}
+
+	// 0x24
+	virtual int writeMultipleBlocks( 
+		uchar_t *pucUID,		// NULL表示select模式
+		uchar_t ucBlkno,		// 开始block no
+		uchar_t blocknum,		// 实际写的block是:blocknum+1;
+		uchar_t ucBlksize,
+		uchar_t buf[] )
+	{
+		return writeBlocks( ucBlkno, blocknum, buf, pucUID );
+	}
+	// 返回UID的长度
+	int StringToHex( char *str, uchar_t aucUID[] );
+	bool opened( )
+	{
+		return m_bComOpened;
+	}
+
+protected:
+	int doInventory( char *pTagUID[] );
+	// CMD: 0x2B
+	// 
+protected:
+	void HexToString( uchar_t aucDat[], int len, char *str );
+	int getValueFromChar( char c );
+
+	// 数据保存方式:
+	// block从 L->H
+	// 每个block中,从byte 0 - byte N-1
+	// 返回:<0 失败
+	// block序号,从0开始.
+	// pTagUID = NULL,表示采用select模式。
+	// nBlockNum: 1...
+	int readBlocks( int nFirstBlock, int nBlockNum, uchar_t *pDat, uchar_t *pSecurity, uchar_t *pucTagUID = NULL );
+	int writeBlocks( int nFirstBlock, int nBlockNum, uchar_t *pDat,uchar_t *pucTagUID = NULL );
+
+	// nxp dosn't support write multiblocks;
+	int writeBlock( int nFirstBlock, uchar_t *pDat,uchar_t *pucTagUID = NULL );
+
+
+	int getVICCstr( char *szUID )
+	{
+		uchar_t aucUID[8];
+		if( StringToHex( szUID, aucUID ))
+		{
+			return getVICC( aucUID );
+		}
+		return -1;
+	}
+
+	bool setVICCStr( char *szUID )
+	{
+		uchar_t aucUID[8];
+		if( StringToHex( szUID, aucUID ))
+		{
+			return setVICC( aucUID );
+		}
+		return false;
+	}
+protected:
+	bool m_bComOpened;
+};
+
+#endif;
diff --git a/databaseAPI.cpp b/databaseAPI.cpp
new file mode 100644
index 0000000..372ed5b
--- /dev/null
+++ b/databaseAPI.cpp
@@ -0,0 +1,64 @@
+#include "databaseAPI.h"
+
+Database::Database(QSqlDatabase database)
+{
+    db = database;
+    db.setDatabaseName(databaseName);
+    db.setUserName(userName);
+}
+
+Database::Database(QSqlDatabase database, QString hostName, int port, QString password)
+{
+    db = database;
+    db.setDatabaseName(databaseName);
+    db.setUserName(userName);
+    db.setHostName(hostName);
+    db.setPort(port);
+    db.setPassword(password);
+}
+
+QSqlDatabase Database::getDatabase()
+{
+    return db;
+}
+
+void Database::setHostName(QString hostName)
+{
+    db.setHostName(hostName);
+}
+
+QString Database::getHostName()
+{
+    return db.hostName();
+}
+
+void Database::setPort(int port)
+{
+    db.setPort(port);
+}
+
+int Database::getPort()
+{
+    return db.port();
+}
+
+void Database::setPassword(QString password)
+{
+    db.setPassword(password);
+}
+
+bool Database::is_connected()
+{
+    return connected;
+}
+
+bool Database::open()
+{
+    connected = db.open();
+    return connected;
+}
+
+Database::~Database()
+{
+    db.close();
+}
diff --git a/databaseAPI.h b/databaseAPI.h
index f5bb7cb..504ea63 100644
--- a/databaseAPI.h
+++ b/databaseAPI.h
@@ -15,68 +15,17 @@ private:
     QString userName = QString("cardManageSystem");
 
 public:
-    Database(QSqlDatabase database)
-    {
-        db = database;
-        db.setDatabaseName(databaseName);
-        db.setUserName(userName);
-    }
-
-    Database(QSqlDatabase database, QString hostName, int port, QString password)
-    {
-        db = database;
-        db.setDatabaseName(databaseName);
-        db.setUserName(userName);
-        db.setHostName(hostName);
-        db.setPort(port);
-        db.setPassword(password);
-    }
-
-    QSqlDatabase getDatabase()
-    {
-        return db;
-    }
-
-    void setHostName(QString hostName)
-    {
-        db.setHostName(hostName);
-    }
-
-    QString getHostName()
-    {
-        return db.hostName();
-    }
-
-    void setPort(int port)
-    {
-        db.setPort(port);
-    }
-
-    int getPort()
-    {
-        return db.port();
-    }
-
-    void setPassword(QString password)
-    {
-        db.setPassword(password);
-    }
-
-    bool is_connected()
-    {
-        return connected;
-    }
-
-    bool open()
-    {
-        connected = db.open();
-        return connected;
-    }
-
-    ~Database()
-    {
-        db.close();
-    }
+    Database(QSqlDatabase database);
+    Database(QSqlDatabase database, QString hostName, int port, QString password);
+    QSqlDatabase getDatabase();
+    void setHostName(QString hostName);
+    QString getHostName();
+    void setPort(int port);
+    int getPort();
+    void setPassword(QString password);
+    bool is_connected();
+    bool open();
+    ~Database();
 };
 
 
diff --git a/deviceAPI.cpp b/deviceAPI.cpp
new file mode 100644
index 0000000..6386828
--- /dev/null
+++ b/deviceAPI.cpp
@@ -0,0 +1,41 @@
+#include "deviceAPI.h"
+
+bool Device::is_connected()
+{
+    return connected;
+}
+
+
+bool Device::is_depositAllowed()
+{
+    return depositAllowed;
+}
+
+
+void Device::setDevice(QString name, Database *db)
+{
+    QSqlQuery query(db->getDatabase());
+    QString sql = QString("select * from device where `name` = '%1';").arg(name);
+    query.exec(sql);
+    if (query.next())
+    {
+        connected = true;
+        this->name = name;
+        depositAllowed = query.value(2).toBool();
+    }
+    else
+    {
+        connected = false;
+        depositAllowed = false;
+    }
+}
+
+
+QString Device::getName()
+{
+    if (connected) {
+        if (depositAllowed) return name + QString("(可充值)");
+        else return name + QString("(仅可消费)");
+    }
+    else return QString("未指定设备名");
+}
diff --git a/deviceAPI.h b/deviceAPI.h
index 712abe5..09d2c06 100644
--- a/deviceAPI.h
+++ b/deviceAPI.h
@@ -13,42 +13,10 @@ private:
     QString name = QString("未指定设备名");
 
 public:
-    bool is_connected()
-    {
-        return connected;
-    }
-
-    bool is_depositAllowed()
-    {
-        return depositAllowed;
-    }
-
-    void setDevice(QString name, Database *db)
-    {
-        QSqlQuery query(db->getDatabase());
-        QString sql = QString("select * from device where `name` = '%1';").arg(name);
-        query.exec(sql);
-        if (query.next())
-        {
-            connected = true;
-            this->name = name;
-            depositAllowed = query.value(2).toBool();
-        }
-        else
-        {
-            connected = false;
-            depositAllowed = false;
-        }
-    }
-
-    QString getName()
-    {
-        if (connected) {
-            if (depositAllowed) return name + QString("(可充值)");
-            else return name + QString("(仅可消费)");
-        }
-        else return QString("未指定设备名");
-    }
+    bool is_connected();
+    bool is_depositAllowed();
+    void setDevice(QString name, Database *db);
+    QString getName();
 };
 
 #endif // DEVICEAPI_H
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 9ad91af..d5dba74 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -1,8 +1,8 @@
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
-#include "settingPage.h"
-#include "quitAppPage.h"
-#include "newCardPage.h"
+#include "settingPage.cpp"
+#include "quitAppPage.cpp"
+#include "newCardPage.cpp"
 
 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
@@ -36,6 +36,12 @@ MainWindow::MainWindow(QWidget *parent)
     deviceLabel = new QLabel(device.getName());
     ui->statusBar->addWidget(deviceLabel);
 
+
+    // 清空部分输入框
+    ui->userIdBox->clear();
+
+
+    // 设置启动页面
     ui->stackedWidget->setCurrentWidget(ui->settingPage);
 }
 
diff --git a/mainwindow.ui b/mainwindow.ui
index 2fac4c6..498ceb5 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -277,7 +277,7 @@
       </property>
       <layout class="QGridLayout" name="gridLayout_2">
        <item row="1" column="1">
-        <widget class="QSpinBox" name="spinBox">
+        <widget class="QSpinBox" name="userIdBox">
          <property name="minimumSize">
           <size>
            <width>200</width>
@@ -290,6 +290,12 @@
            <height>16777215</height>
           </size>
          </property>
+         <property name="minimum">
+          <number>1000</number>
+         </property>
+         <property name="maximum">
+          <number>99999999</number>
+         </property>
         </widget>
        </item>
        <item row="0" column="1">
diff --git a/newCardPage.h b/newCardPage.cpp
similarity index 64%
rename from newCardPage.h
rename to newCardPage.cpp
index 1663aca..6e75697 100644
--- a/newCardPage.h
+++ b/newCardPage.cpp
@@ -1,6 +1,3 @@
-#ifndef NEWCARDPAGE_H
-#define NEWCARDPAGE_H
-
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 
@@ -14,7 +11,10 @@ void MainWindow::on_NewCardAction_triggered()
     if (!ready())
     {
         QMessageBox::warning(this, QString("提示"), QString("读卡器或数据库未连接,请设置。"));
-        ui->stackedWidget->setCurrentWidget(ui->settingPage);
+        if (ui->stackedWidget->currentWidget() != ui->settingPage)
+        {
+            ui->stackedWidget->setCurrentWidget(ui->settingPage);
+        }
         return;
     }
 
@@ -28,8 +28,7 @@ void MainWindow::on_NewCardAction_triggered()
 */
 void MainWindow::on_inventoryButton_clicked()
 {
-
+    QStringList cardIdList = reader.inventory(10); // 最多显示10张卡
+    ui->cardIdBox->clear();
+    ui->cardIdBox->addItems(cardIdList);
 }
-
-
-#endif // NEWCARDPAGE_H
diff --git a/quitAppPage.h b/quitAppPage.cpp
similarity index 84%
rename from quitAppPage.h
rename to quitAppPage.cpp
index 49da04e..7ade2e4 100644
--- a/quitAppPage.h
+++ b/quitAppPage.cpp
@@ -1,6 +1,3 @@
-#ifndef QUITAPPPAGE_H
-#define QUITAPPPAGE_H
-
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 
@@ -23,6 +20,3 @@ void MainWindow::on_confirmQuitButton_clicked()
 {
     this->close();
 }
-
-
-#endif // QUITAPPPAGE_H
diff --git a/readerAPI.cpp b/readerAPI.cpp
new file mode 100644
index 0000000..f6a9800
--- /dev/null
+++ b/readerAPI.cpp
@@ -0,0 +1,46 @@
+#include "readerAPI.h"
+
+
+bool Reader::is_connected()
+{
+    return comNumber > 0;
+}
+
+
+bool Reader::connect()
+{
+    if (CVCDOurs::connectReaderByCOM(comNumber))
+    {
+        return true;
+    }
+    else
+    {
+        comNumber = -1;
+        return false;
+    }
+}
+
+void Reader::setComNumber(int comNumber)
+{
+    this->comNumber = comNumber;
+}
+
+int Reader::getComNumber()
+{
+    return comNumber;
+}
+
+QStringList Reader::inventory(int maxViccNum)
+{
+    uchar_t (*aucUID)[8] = (uchar_t (*)[8])malloc(maxViccNum * sizeof(*aucUID));;
+    int receivedViccNum = CVCDOurs::inventory(false, '\0', maxViccNum, nullptr, aucUID);
+    QStringList uidList;
+    for (int i = 0; i < receivedViccNum; ++i)
+    {
+        char uidStr[8];
+        CVCDOurs::HexToString(aucUID[i], 8, uidStr);
+        uidList.push_back(QString::fromStdString(uidStr));
+    }
+    free(aucUID);
+    return uidList;
+}
diff --git a/readerAPI.h b/readerAPI.h
index c3b3e2a..0270116 100644
--- a/readerAPI.h
+++ b/readerAPI.h
@@ -2,42 +2,25 @@
 #define READERAPI_H
 
 #include <HF15693.h>
+#include <cstdio>
+#include <cstring>
+#include <QString>
+#include <QStringList>
+#include <VCDOurs.h>
 
+typedef unsigned char uchar_t;
 
-class Reader
+class Reader : private CVCDOurs
 {
 private:
     int comNumber = -1; // com口号,若未连接为-1
 
 public:
-    bool is_connected()
-    {
-        return comNumber > 0;
-    }
-
-    bool connectReader()
-    {
-        if (t15portOpen(comNumber))
-        {
-            return true;
-        }
-        else
-        {
-            comNumber = -1;
-            return false;
-        }
-    }
-
-    void setComNumber(int comNumber)
-    {
-        this->comNumber = comNumber;
-    }
-
-    int getComNumber()
-    {
-        return comNumber;
-    }
+    bool is_connected();
+    bool connect();
+    void setComNumber(int comNumber);
+    int getComNumber();
+    QStringList inventory(int maxViccNum);
 };
 
-
 #endif // READERAPI_H
diff --git a/settingPage.h b/settingPage.cpp
similarity index 96%
rename from settingPage.h
rename to settingPage.cpp
index 2539c74..7989acc 100644
--- a/settingPage.h
+++ b/settingPage.cpp
@@ -1,6 +1,3 @@
-#ifndef SETTINGPAGE_H
-#define SETTINGPAGE_H
-
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 
@@ -45,7 +42,7 @@ void MainWindow::on_connectReaderButton_clicked()
 {
     int comNumber = ui->comNumberBox->value();
     reader.setComNumber(comNumber);
-    bool connectSuccess = reader.connectReader();
+    bool connectSuccess = reader.connect();
 
     if (!connectSuccess) {
         QString warningMessage = QString("COM") + QString::number(comNumber);
@@ -105,6 +102,3 @@ bool MainWindow::ready()
     if (!device.is_connected()) return false;
     return true;
 }
-
-
-#endif // SETTINGPAGE_H