2024-08-03 00:49:42 +08:00

319 lines
11 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "readerAPI.h"
#include "qdebug.h"
/**
* @brief 判断读卡器是否已连接
* 通过comNumber是否大于0判断。
* @param void
* @return 读卡器连接状态
* - true 已连接
* - false 未连接
* @author 柯劲帆
* @date 2024-07-27
*/
/// \brief 判断读卡器是否已连接
/// \return 如果comNumber大于0表示已连接返回true否则返回false
bool Reader::is_connected()
{
return comNumber > 0;
}
/**
* @brief 连接读卡器
* 尝试通过comNumber连接读卡器。如果连接成功返回true否则返回false并重置comNumber为-1。
* @param void
* @return 连接成功返回true失败返回false
* @author 柯劲帆
* @date 2024-07-27
*/
bool Reader::connect()
{
if (connectReaderByCOM(comNumber))
{
return true;
}
else
{
comNumber = -1;
return false;
}
}
/**
* @brief 关闭连接
* @param void
* @return void
* @author 柯劲帆
* @date 2024-07-31
*/
void Reader::disconnect()
{
disconnectReaderByCOM();
}
/**
* @brief 设置COM口号
* @param comNumber 要设置的COM口号
* @return void
* @author 柯劲帆
* @date 2024-07-27
*/
void Reader::setComNumber(int comNumber)
{
this->comNumber = comNumber;
}
/**
* @brief 获取当前COM口号
* @param void
* @return 当前的COM口号
* - -1 未连接
* - >0 已连接
* @author 柯劲帆
* @date 2024-07-27
*/
int Reader::getComNumber()
{
return comNumber;
}
/**
* @brief 获取读卡器中卡片的UID列表
* 通过读卡器的inventory方法获取最大数量为maxViccNum的卡片UID并将其转换为QStringList返回。
* @param maxViccNum 最大卡片数量
* @return 包含卡片UID的QStringList
* @details
* 通过读卡器的inventory方法获取最大数量为maxViccNum的卡片UID并将其转换为QStringList返回。
* 该函数分配内存来存储卡片的UID然后调用CVCDOurs::inventory来获取卡片信息。
* 获取到的UID会转换为字符串并添加到QStringList中最后返回该列表。
* @author 柯劲帆
* @date 2024-07-29
*/
QStringList Reader::inventory(int maxViccNum)
{
// 分配内存用于存储卡片的UID
uchar_t (*aucUID)[8] = (uchar_t (*)[8])malloc(maxViccNum * sizeof(*aucUID));;
// 调用CVCDOurs::inventory方法获取卡片数量
int receivedViccNum = CVCDOurs::inventory(false, '\0', maxViccNum, nullptr, aucUID);
// 用于存储UID的QStringList
QStringList uidList;
for (int i = 0; i < receivedViccNum; ++i)
{
char uidStr[8];
// 将UID从十六进制转换为字符串
CVCDOurs::HexToString(aucUID[i], 8, uidStr);
// 将字符串转换为QString并添加到uidList中
uidList.push_back(QString::fromStdString(uidStr));
}
// 释放分配的内存
free(aucUID);
return uidList;
}
/**
* @brief 读取记录数量和最近一条记录的index
* 该函数用于从卡片的block0中读取记录数量和最近一条记录的索引。
* @param recordNum 记录数量,通过引用返回,类型为 int
* @param recordIndex 最近一条记录的索引,通过引用返回,类型为 int
* @param cardId 要读取记录的卡片ID类型为 QString
* @return bool 是否读取成功
* - true 读取成功
* - false 读取失败
* @details
* 函数首先将卡片ID转换为 `uchar_t` 类型的字符数组,然后调用 `readBlocks` 函数从卡片的block0中读取记录数量和索引。
* 如果 `readBlocks` 返回的hex数量为0则表示读取失败函数返回 `false`。
* 否则,函数将读取的结果转换为整数,并通过引用参数 `recordNum` 和 `recordIndex` 返回。
* @author 柯劲帆
* @date 2024-07-31
*/
bool Reader::readRecordNumber(int &recordNum, int &recordIndex, QString cardId)
{
// 记录数量在block0的byte0的bit0-3中
// 最近一条记录的index在block0的byte0的bit4-7中
// 分配内存用于存储读取结果字符串
uchar_t recordIndexHex[4] = {0}; // 一个block有4个byte1个byte有两个hex会返回8个hex存在4个uchar_t中
uchar_t cardIdHex[8] = {0};
StringToHex(cardId.toLatin1().data(), cardIdHex);
int hexNum = readSingleBlock(cardIdHex, 0, 0, recordIndexHex, nullptr);
if (hexNum == 0) return false;
recordNum = (int)(recordIndexHex[0] & 0x0F);
recordIndex = (int)(recordIndexHex[0] >> 4);
return true;
}
/**
* @brief 写入记录数量和最近一条记录的index
* 该函数用于将记录数量和最近一条记录的索引写入到卡片的block0中。
* @param recordNum 记录数量,类型为 int
* @param recordIndex 最近一条记录的索引,类型为 int
* @param cardId 要写入记录的卡片ID类型为 QString
* @return bool 是否写入成功
* - true 写入成功
* - false 写入失败
* @details
* 函数首先将记录数量和记录索引转换为 `uchar_t` 类型,并存储在 `recordIndexStr` 数组中。
* 然后函数将卡片ID转换为 `uchar_t` 类型的字符数组,并调用 `writeBlock` 函数将记录数量和索引写入到卡片的block0中。
* 如果 `writeBlock` 函数返回的写入行数为1则表示写入成功函数返回 `true`;否则,函数返回 `false`。
* @author 柯劲帆
* @date 2024-07-31
*/
bool Reader::writeRecordNumber(int recordNum, int recordIndex, QString cardId)
{
// 记录数量在block0的byte0的bit0-3中
// 最近一条记录的index在block0的byte0的bit4-7中
// 分配内存用于存储读取结果字符串
uchar_t recordIndexStr[4] = {0};
recordIndexStr[0] = (uchar_t)(recordIndex << 4);
recordIndexStr[0] += (uchar_t)(recordNum);
uchar_t cardIdHex[8] = {0};
StringToHex(cardId.toLatin1().data(), cardIdHex);
int success = writeSingleBlock(cardIdHex, 0, 4, recordIndexStr);
return success == 1;
}
/**
* @brief 插入一条交易记录
* 该函数用于将一条新的交易记录插入到卡片中。
* @param record 要插入的交易记录字符串,类型为 QString
* @param cardId 卡片ID类型为 QString
* @return bool 是否插入成功
* - true 插入成功
* - false 插入失败
* @details
* 函数首先读取卡片的当前记录数量和记录索引。如果读取失败,函数将返回 `false`。
* 然后,函数更新记录数量和记录索引,并将新的交易记录转换为 `uchar_t` 类型的字符数组。
* 最后,函数调用 `writeBlocks` 将记录写入卡片。如果写入行数不为0则表示插入成功函数返回 `true`;否则,函数返回 `false`。
* @author 柯劲帆
* @date 2024-07-31
*/
bool Reader::insertRecord(QString record, QString cardId)
{
int recordNum, recordIndex;
bool success = readRecordNumber(recordNum, recordIndex, cardId);
if (!success) return false;
recordNum = std::min(maxRecordNum, recordNum + 1);
recordIndex = (recordIndex + 1) % maxRecordNum;
success = writeRecordNumber(recordNum, recordIndex, cardId);
if (!success) return false;
int blockIndex = 1 + 4 * recordIndex;
uchar_t cardIdHex[8] = {0};
StringToHex(cardId.toLatin1().data(), cardIdHex);
uchar_t recordHex[4 * 4] = {0};
StringToHex(record.toLatin1().data(), recordHex);
int writeLineNumber = writeMultipleBlocks(cardIdHex, blockIndex, 4, 4, recordHex);
return writeLineNumber != 0;
}
/**
* @brief 获取全部记录
* 该函数用于从卡片中获取所有记录。
* @param cardId 要读取记录的卡片ID类型为 QString
* @param ok 操作是否成功的标志,类型为 bool 的引用
* @return QStringList 包含所有记录的字符串列表
* @details
* 函数首先读取卡片的记录数量和起始索引。如果读取失败,函数将设置 `ok` 为 `false` 并返回空列表。
* 然后函数将卡片ID转换为 `uchar_t` 类型的字符数组,并逐个读取记录块。
* 每条记录由4个block组成每个block包含8个hex字符。函数将每个block的内容转换为字符串并拼接成完整的记录字符串。
* 最终,函数将所有记录字符串添加到 `QStringList` 中并返回。
* @author 柯劲帆
* @date 2024-07-31
*/
QStringList Reader::readAllRecords(QString cardId, bool &ok)
{
QStringList recordList;
int recordNum, recordIndex;
bool success = readRecordNumber(recordNum, recordIndex, cardId);
if (!success)
{
ok = false;
return recordList;
}
int recordStartIndex = ((recordIndex - recordNum + 1) % maxRecordNum + maxRecordNum) % maxRecordNum;
uchar_t cardIdHex[8] = {0};
StringToHex(cardId.toLatin1().data(), cardIdHex);
for (int i = 0; i < recordNum; i++)
{
StringToHex(cardId.toLatin1().data(), cardIdHex); // readMultipleBlocks不知为何会改变cardIdHex的值要重新赋值
int recordBlockIndex = 1 + 4 * ((recordStartIndex + i) % maxRecordNum);
uchar_t recordHex[4 * 4] = {0};
int hexNum = readMultipleBlocks(cardIdHex, 0, recordBlockIndex, 4, recordHex, nullptr);
if (hexNum == 0)
{
// 原来的处理代码
// ok = false;
// return recordList;
// 这里经常出现hexNum == 0循环终止的情况原因是返回的reply为空值: “[]”
// 一旦出现[],后面的值大概率会出错。
// 出现主要集中在第二遍循环时。
// 尝试解决方案每一遍循环重新inventory重置一下环境怀疑某些变量被改了、每一遍循环睡眠2秒都没有解决这个问题
// 因此在发生错误时直接返回能够正确读取的记录ID。
break;
}
char recordStr[33] = {0};
HexToString(recordHex, 4 * 4, recordStr);
recordStr[30] = '\0';
QString qRecordStr = QString(recordStr).toUpper();
recordList.push_back(qRecordStr);
}
ok = true;
return recordList;
}
/**
* @brief 卡初始化
* 将第1个block的记录数量设置为0最近一条交易记录下标设置为maxRecordNum-1。
* @param cardId 要初始化的卡片ID类型为 QString
* @return bool 是否初始化成功
* - true 初始化成功
* - false 初始化失败
* @details
* 函数首先将传入的 `QString` 类型的卡片ID转换为 `uchar_t` 类型的字符数组。
* 然后函数定义一个全为0的字符数组并调用 `writeBlocks` 函数将其写入卡片的第1个block。
* 如果 `writeBlocks` 函数返回的写入行数不为0则表示初始化成功函数返回 `true`;否则,函数返回 `false`。
* @author 柯劲帆
* @date 2024-07-31
*/
bool Reader::initCard(QString cardId)
{
uchar_t cardIdHex[8] = {0};
StringToHex(cardId.toLatin1().data(), cardIdHex);
uchar_t initHex[4] = {0};
initHex[0] = (char)(maxRecordNum - 1) << 4;
int writeLineNumber = writeSingleBlock(cardIdHex, 0, 4, initHex);
return writeLineNumber != 0;
}