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 +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 + +// +// 并部分实现函数的部分功能 +// 比如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 +#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 @@ - + 200 @@ -290,6 +290,12 @@ 16777215 + + 1000 + + + 99999999 + 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 +#include +#include +#include +#include +#include +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