|
|
#include <iostream>
|
|
|
#include <iomanip>
|
|
|
#include <algorithm>
|
|
|
#include <cerrno>
|
|
|
#include <unistd.h>
|
|
|
#include <sys/ioctl.h>
|
|
|
#include <termios.h>
|
|
|
#include <sstream>
|
|
|
|
|
|
#include "MsgHandler.hpp"
|
|
|
#include "DEBUG.hpp"
|
|
|
#include "../common/SerialMsgHandler.hpp"
|
|
|
|
|
|
#include <thread>
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
#define BEEP_CARD 1
|
|
|
#define FIND_CARD 2
|
|
|
#define SET_PWD 3
|
|
|
#define READ_CARD 4
|
|
|
#define WRITE_CARD 5
|
|
|
#define STOP_CARD 8
|
|
|
#define WRITE_BLOCK_ID 0x04 // 写数据的块号
|
|
|
|
|
|
namespace
|
|
|
{
|
|
|
const char* PendingCommandName(PendingCommand command)
|
|
|
{
|
|
|
switch (command)
|
|
|
{
|
|
|
case PendingCommand::Find: return "FIND";
|
|
|
case PendingCommand::SetPassword: return "SET_PASSWORD";
|
|
|
case PendingCommand::ReadBlock: return "READ_BLOCK";
|
|
|
case PendingCommand::WriteBlock: return "WRITE_BLOCK";
|
|
|
case PendingCommand::Stop: return "STOP";
|
|
|
case PendingCommand::Beep: return "BEEP";
|
|
|
default: return "NONE";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
uint16_t MapDeviceError(uint8_t command, uint8_t status)
|
|
|
{
|
|
|
if (status == 0) return 0;
|
|
|
if (status == 0x0B) return 0x0012; // authentication failure
|
|
|
if (status == 0x0C) return command == READ_CARD ? 0x0013 : 0x0014;
|
|
|
if (status == 0x0A) return 0x001A; // invalid block
|
|
|
return command == WRITE_CARD ? 0x0014 : 0x0013;
|
|
|
}
|
|
|
std::string CardStateName(CardState state)
|
|
|
{
|
|
|
switch (state)
|
|
|
{
|
|
|
case CardState::AUTO_FIND: return "AUTO_FIND";
|
|
|
case CardState::WAIT_DDS_READ: return "WAIT_DDS_READ";
|
|
|
case CardState::WAIT_DDS_WRITE: return "WAIT_DDS_WRITE";
|
|
|
case CardState::WAIT_DDS_DECISION: return "WAIT_DDS_DECISION";
|
|
|
case CardState::WAIT_DDS_WRITE_NEXT: return "WAIT_DDS_WRITE_NEXT";
|
|
|
}
|
|
|
return "UNKNOWN";
|
|
|
}
|
|
|
|
|
|
std::string HexBytes(const std::vector<uint8_t>& bytes)
|
|
|
{
|
|
|
std::ostringstream out;
|
|
|
out << std::hex << std::uppercase << std::setfill('0');
|
|
|
for (uint8_t byte : bytes)
|
|
|
{
|
|
|
out << std::setw(2) << static_cast<int>(byte) << ' ';
|
|
|
}
|
|
|
return out.str();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
MsgHandler::MsgHandler() : fd(-1), m_CardState(CardState::AUTO_FIND),
|
|
|
m_CurrentIndex(0),
|
|
|
m_ResultCode(0), m_BlockCount(0),
|
|
|
m_BlockOpStartTime(std::chrono::steady_clock::now()),
|
|
|
m_CurrentOperatingBlock(0),
|
|
|
m_PendingReadResponse(std::vector<uint8_t>()),
|
|
|
OnCardDetected(nullptr)
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::OpenPort(const std::string& port, const std::string& baudrate)
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::ClosePort()
|
|
|
{
|
|
|
if (fd != -1)
|
|
|
{
|
|
|
close(fd);
|
|
|
fd = -1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::SetDevice(const std::string& device)
|
|
|
{
|
|
|
this->device = device;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
int MsgHandler::SendDeviceMsg(const std::vector<uint8_t>& data)
|
|
|
{
|
|
|
DEBUG("SERIAL TX device=" << this->device << " size=" << data.size()
|
|
|
<< " bytes=" << HexBytes(data));
|
|
|
|
|
|
int bytesWritten = SendMsg(data);
|
|
|
DEBUG("SERIAL TX result=" << bytesWritten << "/" << data.size());
|
|
|
return bytesWritten;
|
|
|
}
|
|
|
|
|
|
int MsgHandler::RecvDeviceMsg(std::vector<uint8_t>& data, int timeoutMs)
|
|
|
{
|
|
|
int bytesRead = RecvMsg(data, timeoutMs);
|
|
|
|
|
|
if (bytesRead > 0)
|
|
|
{
|
|
|
DEBUG("SERIAL RX device=" << this->device << " size=" << data.size()
|
|
|
<< " bytes=" << HexBytes(data));
|
|
|
}
|
|
|
return bytesRead;
|
|
|
}
|
|
|
|
|
|
int MsgHandler::SendMsg(const std::vector<uint8_t>& data)
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
|
|
|
if (fd == -1)
|
|
|
{
|
|
|
perror("设备未打开");
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
size_t totalWritten = 0;
|
|
|
while (totalWritten < data.size())
|
|
|
{
|
|
|
const ssize_t written = write(fd, data.data() + totalWritten,
|
|
|
data.size() - totalWritten);
|
|
|
if (written < 0)
|
|
|
{
|
|
|
perror("发送数据失败");
|
|
|
return -1;
|
|
|
}
|
|
|
if (written == 0)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
totalWritten += static_cast<size_t>(written);
|
|
|
}
|
|
|
|
|
|
return static_cast<int>(totalWritten);
|
|
|
}
|
|
|
|
|
|
int MsgHandler::RecvMsg(std::vector<uint8_t>& data, int timeoutMs)
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
|
|
|
if (fd == -1)
|
|
|
{
|
|
|
perror("设备未打开\n");
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
const auto deadline = std::chrono::steady_clock::now()
|
|
|
+ std::chrono::milliseconds(std::max(0, timeoutMs));
|
|
|
|
|
|
while (true)
|
|
|
{
|
|
|
if (TryExtractDeviceFrame(data))
|
|
|
{
|
|
|
return static_cast<int>(data.size());
|
|
|
}
|
|
|
|
|
|
const auto remaining = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
deadline - std::chrono::steady_clock::now()).count();
|
|
|
if (remaining <= 0)
|
|
|
{
|
|
|
// 兼容现场设备短读帧:只要块号和 16 字节数据已经到齐,
|
|
|
// 即 LEN + header + block + data 至少 21 字节,就交给读卡解析。
|
|
|
if (m_ReceiveBuffer.size() >= 21U
|
|
|
&& m_ReceiveBuffer[1] == 0x01U
|
|
|
&& m_ReceiveBuffer[2] == READ_CARD
|
|
|
&& m_ReceiveBuffer[3] == 0x00U)
|
|
|
{
|
|
|
data = m_ReceiveBuffer;
|
|
|
m_ReceiveBuffer.clear();
|
|
|
DEBUG("SERIAL RX short READ_BLOCK frame accepted for data extraction, size="
|
|
|
<< data.size() << " bytes=" << HexBytes(data));
|
|
|
return static_cast<int>(data.size());
|
|
|
}
|
|
|
if (!m_ReceiveBuffer.empty())
|
|
|
{
|
|
|
const std::string incomplete = HexBytes(m_ReceiveBuffer);
|
|
|
const size_t bufferedSize = m_ReceiveBuffer.size();
|
|
|
m_ReceiveBuffer.clear();
|
|
|
DEBUG("SERIAL RX incomplete frame timeout buffered="
|
|
|
<< bufferedSize << " bytes=" << incomplete);
|
|
|
if (RetryCurrentReadBlock("incomplete frame"))
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
fd_set readfds;
|
|
|
struct timeval tv;
|
|
|
FD_ZERO(&readfds);
|
|
|
FD_SET(fd, &readfds);
|
|
|
tv.tv_sec = static_cast<long>(remaining / 1000);
|
|
|
tv.tv_usec = static_cast<long>((remaining % 1000) * 1000);
|
|
|
|
|
|
const int ret = select(fd + 1, &readfds, nullptr, nullptr, &tv);
|
|
|
if (ret < 0)
|
|
|
{
|
|
|
if (errno == EINTR)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
perror("select错误");
|
|
|
return -1;
|
|
|
}
|
|
|
if (ret == 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
int available = 0;
|
|
|
if (ioctl(fd, FIONREAD, &available) < 0)
|
|
|
{
|
|
|
perror("ioctl FIONREAD失败");
|
|
|
return -1;
|
|
|
}
|
|
|
if (available <= 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
std::vector<uint8_t> received(static_cast<size_t>(available));
|
|
|
const ssize_t bytesRead = read(fd, received.data(), received.size());
|
|
|
if (bytesRead < 0)
|
|
|
{
|
|
|
if (errno == EINTR)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
perror("读取数据失败");
|
|
|
return -1;
|
|
|
}
|
|
|
if (bytesRead == 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
received.resize(static_cast<size_t>(bytesRead));
|
|
|
const auto rx_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
std::chrono::steady_clock::now().time_since_epoch()).count();
|
|
|
DEBUG("SERIAL RX RAW t_ms=" << rx_ms << " available=" << available
|
|
|
<< " read=" << bytesRead << " buffered_before=" << m_ReceiveBuffer.size()
|
|
|
<< " bytes=" << HexBytes(received));
|
|
|
|
|
|
m_ReceiveBuffer.insert(m_ReceiveBuffer.end(), received.begin(), received.end());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::TryExtractDeviceFrame(std::vector<uint8_t>& data)
|
|
|
{
|
|
|
data.clear();
|
|
|
|
|
|
while (!m_ReceiveBuffer.empty())
|
|
|
{
|
|
|
const size_t expectedSize = static_cast<size_t>(m_ReceiveBuffer.front()) + 1U;
|
|
|
if (expectedSize < 6U || expectedSize > 256U)
|
|
|
{
|
|
|
DEBUG("Discarding invalid device frame length="
|
|
|
<< static_cast<int>(m_ReceiveBuffer.front()));
|
|
|
m_ReceiveBuffer.erase(m_ReceiveBuffer.begin());
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if (m_ReceiveBuffer.size() < expectedSize)
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
std::vector<uint8_t> frame(m_ReceiveBuffer.begin(),
|
|
|
m_ReceiveBuffer.begin() + expectedSize);
|
|
|
m_ReceiveBuffer.erase(m_ReceiveBuffer.begin(),
|
|
|
m_ReceiveBuffer.begin() + expectedSize);
|
|
|
|
|
|
if (!IsValidDeviceFrame(frame))
|
|
|
{
|
|
|
DEBUG("Discarding invalid device frame, length=" << frame.size()
|
|
|
<< " bytes=" << HexBytes(frame));
|
|
|
if (frame.size() >= 4U
|
|
|
&& frame[1] == 0x01U
|
|
|
&& frame[2] == READ_CARD
|
|
|
&& m_PendingCommand == PendingCommand::ReadBlock
|
|
|
&& RetryCurrentReadBlock("CRC validation failure"))
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
data = std::move(frame);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::RetryCurrentReadBlock(const char* reason)
|
|
|
{
|
|
|
if (m_PendingCommand != PendingCommand::ReadBlock
|
|
|
|| !m_BlockOperationPending
|
|
|
|| m_ReadBlockRetryCount >= READ_BLOCK_MAX_RETRIES)
|
|
|
{
|
|
|
DEBUG("Read block retry unavailable: reason=" << reason
|
|
|
<< " attempts=" << static_cast<int>(m_ReadBlockRetryCount)
|
|
|
<< " max=" << static_cast<int>(READ_BLOCK_MAX_RETRIES));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
const uint8_t block = m_PendingBlock;
|
|
|
++m_ReadBlockRetryCount;
|
|
|
DEBUG("Retrying read block=" << static_cast<int>(block)
|
|
|
<< " attempt=" << static_cast<int>(m_ReadBlockRetryCount)
|
|
|
<< "/" << static_cast<int>(READ_BLOCK_MAX_RETRIES)
|
|
|
<< " reason=" << reason);
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(READ_BLOCK_RETRY_DELAY_MS));
|
|
|
m_ReceiveBuffer.clear();
|
|
|
if (fd != -1)
|
|
|
{
|
|
|
tcflush(fd, TCIFLUSH);
|
|
|
}
|
|
|
DEBUG("Read block retry buffer cleared, block=" << static_cast<int>(block));
|
|
|
ReadCard({block});
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::HasActiveDeviceOperation() const
|
|
|
{
|
|
|
return m_PendingCommand != PendingCommand::None || m_BlockOperationPending;
|
|
|
}
|
|
|
|
|
|
int MsgHandler::HandleFindResponse(const std::vector<uint8_t>& data,
|
|
|
WeighingSystem::ReadCardRsp& rsp)
|
|
|
{
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
if (data[3] != 0)
|
|
|
{
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
rsp.msg()["find"].push_back(data[3]);
|
|
|
return 1;
|
|
|
}
|
|
|
if (data.size() < 10U)
|
|
|
{
|
|
|
DEBUG("FindCard success frame too short, size=" << data.size());
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
const uint32_t cardIdValue = (data[4] << 24) | (data[5] << 16)
|
|
|
| (data[6] << 8) | data[7];
|
|
|
std::stringstream ss;
|
|
|
ss << std::hex << std::uppercase << std::setw(8) << std::setfill('0') << cardIdValue;
|
|
|
m_CardId = ss.str();
|
|
|
m_AutoFind = false;
|
|
|
SetCardState(CardState::WAIT_DDS_READ);
|
|
|
rsp.msg()["find"].insert(rsp.msg()["find"].end(), data.begin() + 4, data.begin() + 8);
|
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(trigger_mtx_);
|
|
|
pending_trigger_card_ = m_CardId;
|
|
|
}
|
|
|
DEBUG("Card found: " << m_CardId << ", ready for DDS read or write command");
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::IsValidDeviceFrame(const std::vector<uint8_t>& data)
|
|
|
{
|
|
|
if (data.size() < 6U)
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
const bool shortReadFrame = data.size() >= 21U
|
|
|
&& data[0] != static_cast<uint8_t>(data.size() - 1U)
|
|
|
&& data[1] == 0x01U
|
|
|
&& data[2] == READ_CARD
|
|
|
&& data[3] == 0x00U;
|
|
|
if (!shortReadFrame && data.size() != static_cast<size_t>(data[0]) + 1U)
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
if (data[1] != 0x01)
|
|
|
{
|
|
|
DEBUG("Invalid device fixed byte=" << std::hex << static_cast<int>(data[1]));
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (shortReadFrame)
|
|
|
{
|
|
|
DEBUG("Accepting short READ_BLOCK frame without length/CRC enforcement, size="
|
|
|
<< data.size());
|
|
|
return true;
|
|
|
}
|
|
|
if (!VERIFY_DEVICE_CRC)
|
|
|
{
|
|
|
DEBUG("CRC_BYPASS device frame accepted, size=" << std::dec << data.size()
|
|
|
<< " cmd=0x" << std::hex << static_cast<int>(data[2]));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
std::vector<uint8_t> crcData(data.begin(), data.end() - 2);
|
|
|
CRC16(crcData);
|
|
|
// 现场实帧确认:响应 CRC 与本地发送方向一致,高字节在前、低字节在后。
|
|
|
const bool valid = crcData[crcData.size() - 2] == data[data.size() - 2]
|
|
|
&& crcData[crcData.size() - 1] == data[data.size() - 1];
|
|
|
if (!valid)
|
|
|
{
|
|
|
DEBUG("Invalid device CRC expected(high/low)="
|
|
|
<< std::hex << static_cast<int>(crcData[crcData.size() - 2]) << "/"
|
|
|
<< static_cast<int>(crcData[crcData.size() - 1])
|
|
|
<< " actual(high/low)=" << static_cast<int>(data[data.size() - 2])
|
|
|
<< "/" << static_cast<int>(data[data.size() - 1]));
|
|
|
}
|
|
|
return valid;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::IsDataBlock(uint8_t block)
|
|
|
{
|
|
|
return block < 64U && (block % 4U) != 3U;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::HandleDdsMsg(const std::map<std::string, std::vector<uint8_t>>& msg,
|
|
|
unsigned long index)
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
if (msg.size() != 1U)
|
|
|
{
|
|
|
DEBUG("Rejecting DDS command: expected exactly one msg key, actual=" << msg.size());
|
|
|
return;
|
|
|
}
|
|
|
auto cmd = msg.begin();
|
|
|
|
|
|
DEBUG("HandleDdsMsg: cmd=" << cmd->first << ", index=" << index);
|
|
|
DEBUG("COMMAND state=" << CardStateName(m_CardState)
|
|
|
<< " auto_find=" << m_AutoFind << " payload_size=" << cmd->second.size());
|
|
|
|
|
|
// ========== read 命令处理 ==========
|
|
|
if (cmd->first == "read")
|
|
|
{
|
|
|
// 【新增】如果在恢复序列执行期间收到了新命令,说明恢复被新命令打断
|
|
|
// 此时要:1)停止旧恢复,2)清空旧状态,3)开始新命令
|
|
|
if (m_IsInTimeoutRecovery) {
|
|
|
DEBUG("HandleDdsMsg: new read command arrived during timeout recovery, aborting old recovery");
|
|
|
// 清除旧的恢复标志(让 SkipCurrentBlock 不要再发了)
|
|
|
m_IsInTimeoutRecovery = false;
|
|
|
}
|
|
|
|
|
|
// 【新增】开始计时
|
|
|
m_ReadStartTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
|
if (m_CardState != CardState::WAIT_DDS_READ)
|
|
|
{
|
|
|
DEBUG("Unexpected read command, current state=" << (int)m_CardState);
|
|
|
return;
|
|
|
}
|
|
|
if (HasActiveDeviceOperation())
|
|
|
{
|
|
|
DEBUG("Rejecting read command: device operation is active");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 解析 read 请求协议:
|
|
|
// 版本(1) | 卡号长度(1) | 卡号ASCII | 块数量(1) | 块号列表
|
|
|
const std::vector<uint8_t>& data = cmd->second;
|
|
|
|
|
|
DEBUG("DDS raw data: size=" << data.size());
|
|
|
for (size_t i = 0; i < data.size() && i < 20; i++) {
|
|
|
std::cout << std::hex << std::setw(2) << std::setfill('0')
|
|
|
<< (int)data[i] << " ";
|
|
|
}
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
if (data.size() < 3)
|
|
|
{
|
|
|
DEBUG("Invalid read request, size=" << data.size());
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
uint8_t version = data[0];
|
|
|
uint8_t cardIdLen = data[1];
|
|
|
if (data.size() < static_cast<size_t>(3U + cardIdLen))
|
|
|
{
|
|
|
DEBUG("Invalid read request card ID length=" << static_cast<int>(cardIdLen));
|
|
|
return;
|
|
|
}
|
|
|
std::string reqCardId(data.begin() + 2, data.begin() + 2 + cardIdLen);
|
|
|
uint8_t blockCount = data[2 + cardIdLen];
|
|
|
if (data.size() < static_cast<size_t>(3U + cardIdLen + blockCount))
|
|
|
{
|
|
|
DEBUG("Invalid read request block count=" << static_cast<int>(blockCount));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
DEBUG("Read req: version=" << (int)version
|
|
|
<< ", cardId=" << reqCardId
|
|
|
<< ", blockCount=" << (int)blockCount);
|
|
|
|
|
|
// 验证卡号(与检测到的卡号匹配)
|
|
|
if (reqCardId != m_CardId)
|
|
|
{
|
|
|
DEBUG("Card ID mismatch: req=" << reqCardId << ", found=" << m_CardId);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 清空读卡缓冲区
|
|
|
ClearReadBuffer();
|
|
|
m_CardId = reqCardId;
|
|
|
|
|
|
// 保存请求的块号列表。块 4、5 是扇区 1 的数据块,不能特殊跳过。
|
|
|
m_RequestedBlocks.clear();
|
|
|
for (int i = 0; i < blockCount; i++)
|
|
|
{
|
|
|
uint8_t blockNum = data[3 + cardIdLen + i];
|
|
|
if (!IsDataBlock(blockNum))
|
|
|
{
|
|
|
DEBUG("Rejecting sector trailer/control block " << static_cast<int>(blockNum));
|
|
|
ClearReadBuffer();
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
return;
|
|
|
}
|
|
|
m_RequestedBlocks.push_back(blockNum);
|
|
|
DEBUG("Queuing block " << (int)blockNum << " for reading");
|
|
|
}
|
|
|
m_BlockCount = static_cast<uint8_t>(m_RequestedBlocks.size());
|
|
|
|
|
|
if (m_RequestedBlocks.empty())
|
|
|
{
|
|
|
DEBUG("Read request contains no blocks");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 【修改】只发送第一个读卡命令
|
|
|
m_CurrentReadIndex = 0;
|
|
|
m_ReadBlockRetryCount = 0;
|
|
|
m_CurrentIndex = index;
|
|
|
if (!m_RequestedBlocks.empty())
|
|
|
{
|
|
|
ReadCard({m_RequestedBlocks[0]});
|
|
|
DEBUG("Sent first read command for block " << (int)m_RequestedBlocks[0]);
|
|
|
}
|
|
|
// 切换到等待读卡完成状态
|
|
|
SetCardState(CardState::WAIT_DDS_READ);
|
|
|
m_StateEnterTime = std::chrono::steady_clock::now();
|
|
|
// 保存 index 以便后续使用
|
|
|
m_CurrentIndex = index;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// ========== write 命令处理 ==========
|
|
|
if (cmd->first == "write")
|
|
|
{
|
|
|
// 已寻到并验证卡号后,读和写都是独立 DDS 操作;写卡不要求先读卡。
|
|
|
if (m_CardState != CardState::WAIT_DDS_READ)
|
|
|
{
|
|
|
DEBUG("Unexpected write command, current state=" << (int)m_CardState);
|
|
|
return;
|
|
|
}
|
|
|
if (HasActiveDeviceOperation())
|
|
|
{
|
|
|
DEBUG("Rejecting write command: device operation is active");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 解析 write 请求协议:
|
|
|
// 版本(1) | 卡号长度(1) | 卡号ASCII | 块号(1) | 16字节块数据
|
|
|
const std::vector<uint8_t>& data = cmd->second;
|
|
|
if (data.size() < 3)
|
|
|
{
|
|
|
DEBUG("Invalid write request, size=" << data.size());
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
uint8_t version = data[0];
|
|
|
uint8_t cardIdLen = data[1];
|
|
|
if (data.size() < static_cast<size_t>(3U + cardIdLen + 16U))
|
|
|
{
|
|
|
DEBUG("Invalid write request length=" << data.size()
|
|
|
<< ", card ID length=" << static_cast<int>(cardIdLen));
|
|
|
return;
|
|
|
}
|
|
|
std::string reqCardId(data.begin() + 2, data.begin() + 2 + cardIdLen);
|
|
|
uint8_t blockNum = data[2 + cardIdLen];
|
|
|
if (!IsDataBlock(blockNum))
|
|
|
{
|
|
|
DEBUG("Rejecting sector trailer/control block " << static_cast<int>(blockNum));
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
DEBUG("Write req: version=" << (int)version
|
|
|
<< ", cardId=" << reqCardId
|
|
|
<< ", block=" << (int)blockNum);
|
|
|
|
|
|
// 验证卡号(先校验,校验通过再切状态)
|
|
|
if (reqCardId != m_CardId)
|
|
|
{
|
|
|
DEBUG("Card ID mismatch: req=" << reqCardId << ", found=" << m_CardId);
|
|
|
return; // ← 此时还是 WAIT_DDS_DECISION,状态不会卡死
|
|
|
}
|
|
|
|
|
|
// 校验通过,进入"等 RC522 写卡响应"状态
|
|
|
m_CurrentIndex = index;
|
|
|
SetCardState(CardState::WAIT_DDS_WRITE);
|
|
|
|
|
|
// 构造写卡数据(块号 + 16字节数据)
|
|
|
std::vector<uint8_t> writeData;
|
|
|
writeData.push_back(blockNum);
|
|
|
writeData.insert(writeData.end(),
|
|
|
data.begin() + 3 + cardIdLen,
|
|
|
data.begin() + 3 + cardIdLen + 16);
|
|
|
|
|
|
DEBUG("Writing block " << (int)blockNum);
|
|
|
WriteCard(writeData);
|
|
|
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// ========== 其他命令保持不变 ==========
|
|
|
if (cmd->first == "find")
|
|
|
{
|
|
|
if (HasActiveDeviceOperation())
|
|
|
{
|
|
|
DEBUG("Ignoring find command while a device operation is active");
|
|
|
return;
|
|
|
}
|
|
|
m_AutoFind = true;
|
|
|
m_PreCard.clear();
|
|
|
m_CardId.clear();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
FindCard();
|
|
|
m_FindIntervalMs = 5000;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
}
|
|
|
else if (cmd->first == "stop")
|
|
|
{
|
|
|
if (HasActiveDeviceOperation())
|
|
|
{
|
|
|
DEBUG("Ignoring stop command while a device operation is active");
|
|
|
return;
|
|
|
}
|
|
|
m_AutoFind = false;
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
StopCard();
|
|
|
}
|
|
|
else if (cmd->first == "beep")
|
|
|
{
|
|
|
if (HasActiveDeviceOperation())
|
|
|
{
|
|
|
DEBUG("Ignoring beep command while a device operation is active");
|
|
|
return;
|
|
|
}
|
|
|
m_AutoFind = false;
|
|
|
BeepCard(cmd->second);
|
|
|
}
|
|
|
else if (cmd->first == "reset")
|
|
|
{
|
|
|
if (HasActiveDeviceOperation())
|
|
|
{
|
|
|
DEBUG("Ignoring reset command while a device operation is active");
|
|
|
return;
|
|
|
}
|
|
|
m_AutoFind = false;
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
ResetCard();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
int MsgHandler::HandleDeviceMsg(WeighingSystem::ReadCardRsp& rsp)
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
std::vector<uint8_t> m_RecvData;
|
|
|
|
|
|
if (RecvDeviceMsg(m_RecvData, 200) > 0)
|
|
|
{
|
|
|
// for (int num : m_RecvData)
|
|
|
// {
|
|
|
// std::cout << std::hex << std::setw(2) << std::setfill('0')
|
|
|
// << num << " ";
|
|
|
// }
|
|
|
// std::cout << std::endl;
|
|
|
return (ParseDeviceMsg(m_RecvData, rsp));
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::CRC16(std::vector<uint8_t>& data)
|
|
|
{
|
|
|
uint8_t chCRCHi = 0xFF;
|
|
|
uint8_t chCRCLo = 0xFF;
|
|
|
|
|
|
for (size_t i = 0; i < data.size(); i++)
|
|
|
{
|
|
|
uint8_t wIndex = chCRCLo ^ data[i];
|
|
|
chCRCLo = chCRCHi ^ chCRCHTalbe[wIndex];
|
|
|
chCRCHi = chCRCLTalbe[wIndex];
|
|
|
}
|
|
|
// 保持当前运行环境既有 CRC 字节序:高字节在前、低字节在后。
|
|
|
data.insert(data.end(), {chCRCHi, chCRCLo});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int MsgHandler::ParseDeviceMsg(std::vector<uint8_t>& data, WeighingSystem::ReadCardRsp& rsp)
|
|
|
{
|
|
|
|
|
|
// 【新增】清理之前的响应消息,避免键累积
|
|
|
rsp.msg().clear();
|
|
|
|
|
|
// 仅接受已完整接收且通过协议校验的帧。
|
|
|
if (!IsValidDeviceFrame(data))
|
|
|
{
|
|
|
DEBUG("ParseDeviceMsg: invalid device frame, size=" << data.size());
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
DEBUG("DEVICE FRAME seq=" << std::dec << m_OperationSequence
|
|
|
<< " expected=" << PendingCommandName(m_PendingCommand)
|
|
|
<< " cmd=0x" << std::hex << static_cast<int>(data[2])
|
|
|
<< " status=0x" << static_cast<int>(data[3])
|
|
|
<< " state=" << CardStateName(m_CardState)
|
|
|
<< " dds_index=" << std::dec << m_CurrentIndex
|
|
|
<< " pending_block=" << static_cast<int>(m_CurrentOperatingBlock)
|
|
|
<< " pending=" << m_BlockOperationPending);
|
|
|
|
|
|
switch (data[2])
|
|
|
{
|
|
|
case BEEP_CARD:
|
|
|
if (m_PendingCommand != PendingCommand::Beep) return 0;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
rsp.msg()["beep"].insert(rsp.msg()["beep"].end(), data[3]);
|
|
|
break;
|
|
|
case STOP_CARD:
|
|
|
if (m_PendingCommand == PendingCommand::Find)
|
|
|
{
|
|
|
return HandleFindResponse(data, rsp);
|
|
|
}
|
|
|
if (m_PendingCommand != PendingCommand::Stop) return 0;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
rsp.msg()["stop"].insert(rsp.msg()["stop"].end(), data[3]);
|
|
|
break;
|
|
|
case FIND_CARD:
|
|
|
if (m_PendingCommand != PendingCommand::Find) return 0;
|
|
|
return HandleFindResponse(data, rsp);
|
|
|
case READ_CARD:
|
|
|
{
|
|
|
if (m_PendingCommand != PendingCommand::ReadBlock)
|
|
|
{
|
|
|
DEBUG("Ignoring unmatched READ_CARD response");
|
|
|
return 0;
|
|
|
}
|
|
|
const uint8_t status = data[3];
|
|
|
if (status != 0)
|
|
|
{
|
|
|
DEBUG("ReadCard FAIL: error code=0x" << std::hex << static_cast<int>(status));
|
|
|
|
|
|
std::vector<uint8_t> rsp_data;
|
|
|
rsp_data.push_back(1);
|
|
|
const uint16_t error = MapDeviceError(READ_CARD, status);
|
|
|
rsp_data.push_back(static_cast<uint8_t>(error >> 8));
|
|
|
rsp_data.push_back(static_cast<uint8_t>(error & 0xFF));
|
|
|
rsp_data.push_back(static_cast<uint8_t>(m_CardId.length()));
|
|
|
rsp_data.insert(rsp_data.end(), m_CardId.begin(), m_CardId.end());
|
|
|
rsp_data.push_back(0);
|
|
|
|
|
|
rsp.msg()["read"] = rsp_data;
|
|
|
ClearReadBuffer();
|
|
|
m_CurrentOperatingBlock = 0;
|
|
|
m_BlockOperationPending = false;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
if (m_RequestedBlocks.empty() || m_CurrentReadIndex >= m_RequestedBlocks.size())
|
|
|
{
|
|
|
DEBUG("ReadCard success without a pending requested block");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
const size_t payloadSize = data.size() - 6U;
|
|
|
uint8_t blockNum = m_RequestedBlocks[m_CurrentReadIndex];
|
|
|
std::vector<uint8_t> block16;
|
|
|
if (data.size() >= 21U)
|
|
|
{
|
|
|
// 兼容参考驱动的固定偏移:data[4] 为块号,data[5..20] 为 16 字节数据。
|
|
|
const uint8_t responseBlock = data[4];
|
|
|
if (responseBlock != blockNum)
|
|
|
{
|
|
|
DEBUG("ReadCard response block mismatch, expected=" << static_cast<int>(blockNum)
|
|
|
<< ", actual=" << static_cast<int>(responseBlock));
|
|
|
return 0;
|
|
|
}
|
|
|
block16.assign(data.begin() + 5, data.begin() + 21);
|
|
|
}
|
|
|
else if (payloadSize == 16U)
|
|
|
{
|
|
|
block16.assign(data.begin() + 4, data.begin() + 20);
|
|
|
}
|
|
|
else if (payloadSize == 17U)
|
|
|
{
|
|
|
const uint8_t responseBlock = data[4];
|
|
|
if (responseBlock != blockNum)
|
|
|
{
|
|
|
DEBUG("ReadCard response block mismatch, expected=" << static_cast<int>(blockNum)
|
|
|
<< ", actual=" << static_cast<int>(responseBlock));
|
|
|
RetryCurrentReadBlock("response block mismatch");
|
|
|
return 0;
|
|
|
}
|
|
|
block16.assign(data.begin() + 5, data.begin() + 21);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
DEBUG("ReadCard success payload length invalid, size=" << payloadSize);
|
|
|
RetryCurrentReadBlock("invalid payload length");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
m_BlockOpStartTime = std::chrono::steady_clock::now();
|
|
|
m_CurrentOperatingBlock = 0;
|
|
|
m_BlockOperationPending = false;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
m_ReadBlockRetryCount = 0;
|
|
|
|
|
|
DEBUG("ReadCard OK, block=" << (int)blockNum);
|
|
|
|
|
|
m_BlockData.push_back({blockNum, block16});
|
|
|
|
|
|
if (m_BlockData.size() >= m_RequestedBlocks.size())
|
|
|
{
|
|
|
// 所有块读取完成,构建完整 DDS 响应
|
|
|
DEBUG("All blocks read, building complete DDS response");
|
|
|
|
|
|
std::vector<uint8_t> rsp_data;
|
|
|
rsp_data.push_back(1); // 版本
|
|
|
rsp_data.push_back(0x00); // 结果码高
|
|
|
rsp_data.push_back(0x00); // 结果码低
|
|
|
rsp_data.push_back(m_CardId.length()); // 卡号长度
|
|
|
rsp_data.insert(rsp_data.end(), m_CardId.begin(), m_CardId.end()); // 卡号
|
|
|
rsp_data.push_back(m_BlockData.size()); // 块数量
|
|
|
|
|
|
// 添加所有块的数据
|
|
|
for (const auto& block : m_BlockData)
|
|
|
{
|
|
|
rsp_data.push_back(block.first); // 块号
|
|
|
rsp_data.insert(rsp_data.end(), block.second.begin(), block.second.end()); // 16字节
|
|
|
}
|
|
|
|
|
|
rsp.msg()["read"] = rsp_data;
|
|
|
|
|
|
// 清理并恢复状态
|
|
|
m_BlockData.clear();
|
|
|
m_RequestedBlocks.clear();
|
|
|
m_CurrentReadIndex = 0;
|
|
|
// 读卡事务结束后保留当前卡 10 秒,期间可继续接收 DDS 读写命令。
|
|
|
m_AutoFind = false;
|
|
|
SetCardState(CardState::WAIT_DDS_READ);
|
|
|
|
|
|
|
|
|
|
|
|
// 【新增】结束计时并打印
|
|
|
m_ReadEndTime = std::chrono::steady_clock::now();
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
m_ReadEndTime - m_ReadStartTime).count();
|
|
|
DEBUG("Read card elapsed: " << elapsed << " ms ("
|
|
|
<< elapsed / 1000.0 << " seconds)");
|
|
|
|
|
|
return 1; // ← 返回1表示需要发布 DDS 响应
|
|
|
}
|
|
|
|
|
|
m_CurrentReadIndex++;
|
|
|
if (m_CurrentReadIndex < m_RequestedBlocks.size())
|
|
|
{
|
|
|
uint8_t nextBlock = m_RequestedBlocks[m_CurrentReadIndex];
|
|
|
DEBUG("Sending next read command for block " << (int)nextBlock);
|
|
|
m_ReadBlockRetryCount = 0;
|
|
|
ReadCard({nextBlock});
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
|
|
}
|
|
|
|
|
|
return 0; // ← 未全部完成,不发布响应
|
|
|
}
|
|
|
break;
|
|
|
case WRITE_CARD:
|
|
|
{
|
|
|
if (m_PendingCommand != PendingCommand::WriteBlock)
|
|
|
{
|
|
|
DEBUG("Ignoring unmatched WRITE_CARD response");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
if (data[3] == 0) // 写卡成功
|
|
|
{
|
|
|
m_BlockOperationPending = false;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
DEBUG("WriteCard OK: block=" << (int)m_PendingWriteBlock);
|
|
|
|
|
|
SetCardState(CardState::WAIT_DDS_READ);
|
|
|
DEBUG("Write complete, card remains ready for next DDS command");
|
|
|
|
|
|
// ★ 与 read 响应同协议:1 键 + vector
|
|
|
std::vector<uint8_t> write_rsp;
|
|
|
write_rsp.push_back(1); // 版本
|
|
|
write_rsp.push_back(0x00); // 结果码高
|
|
|
write_rsp.push_back(0x00); // 结果码低(成功)
|
|
|
write_rsp.push_back((uint8_t)m_CardId.size()); // 卡号长度
|
|
|
write_rsp.insert(write_rsp.end(),
|
|
|
m_CardId.begin(), m_CardId.end()); // 卡号
|
|
|
write_rsp.push_back(1); // 块数量 = 1
|
|
|
write_rsp.push_back(m_PendingWriteBlock); // 块号
|
|
|
write_rsp.push_back(0x00); // 块结果码高 = 0(成功)
|
|
|
write_rsp.push_back(0x00); // 块结果码低 = 0(成功) // 块数量 = 0
|
|
|
rsp.msg()["write"] = write_rsp;
|
|
|
|
|
|
rsp.index(m_CurrentIndex);
|
|
|
return 1;
|
|
|
}
|
|
|
else // 写卡失败
|
|
|
{
|
|
|
m_BlockOperationPending = false;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
DEBUG("WriteCard FAIL: block=" << (int)m_PendingWriteBlock
|
|
|
<< " error code=0x" << std::hex << (int)data[3]);
|
|
|
|
|
|
// ★ 失败也用 1 键 + vector
|
|
|
std::vector<uint8_t> write_rsp;
|
|
|
write_rsp.push_back(1); // 版本
|
|
|
const uint16_t error = MapDeviceError(WRITE_CARD, data[3]);
|
|
|
write_rsp.push_back(static_cast<uint8_t>(error >> 8));
|
|
|
write_rsp.push_back(static_cast<uint8_t>(error & 0xFF)); // 结果码
|
|
|
write_rsp.push_back((uint8_t)m_CardId.size()); // 卡号长度
|
|
|
write_rsp.insert(write_rsp.end(),
|
|
|
m_CardId.begin(), m_CardId.end()); // 卡号
|
|
|
write_rsp.push_back(1); // 块数量 = 1
|
|
|
write_rsp.push_back(m_PendingWriteBlock); // 块号
|
|
|
write_rsp.push_back(static_cast<uint8_t>(error >> 8));
|
|
|
write_rsp.push_back(static_cast<uint8_t>(error & 0xFF)); // 块结果码
|
|
|
rsp.msg()["write"] = write_rsp;
|
|
|
|
|
|
rsp.index(m_CurrentIndex);
|
|
|
return 1;
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
void MsgHandler::StorePasswd(std::map<uint8_t, std::vector<uint8_t>>& data)
|
|
|
{
|
|
|
m_PassWord = data;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::SetPasswd(const std::vector<uint8_t>& data)
|
|
|
{
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // 添加延迟
|
|
|
|
|
|
std::vector<uint8_t> m_SendData;
|
|
|
m_PendingCommand = PendingCommand::SetPassword;
|
|
|
++m_OperationSequence;
|
|
|
|
|
|
m_SendData.insert(m_SendData.end(), {0x0C, 0x01, 0x03, 60, data[0]});
|
|
|
auto it = m_PassWord.find(data[0]);
|
|
|
if (it != m_PassWord.end())
|
|
|
{
|
|
|
m_SendData.insert(m_SendData.end(), it->second.begin(), it->second.end());
|
|
|
}
|
|
|
|
|
|
CRC16(m_SendData);
|
|
|
SendDeviceMsg(m_SendData);
|
|
|
}
|
|
|
|
|
|
void MsgHandler::StopCard(void)
|
|
|
{
|
|
|
std::vector<uint8_t> m_SendData;
|
|
|
m_PendingCommand = PendingCommand::Stop;
|
|
|
++m_OperationSequence;
|
|
|
|
|
|
m_SendData.insert(m_SendData.end(), {0x04, 0x01, 0x08});
|
|
|
CRC16(m_SendData);
|
|
|
SendDeviceMsg(m_SendData);
|
|
|
}
|
|
|
|
|
|
void MsgHandler::FindCard(void)
|
|
|
{
|
|
|
std::vector<uint8_t> m_SendData;
|
|
|
m_PendingCommand = PendingCommand::Find;
|
|
|
++m_OperationSequence;
|
|
|
|
|
|
m_SendData.insert(m_SendData.end(), {0x04, 0x01, 0x02});
|
|
|
CRC16(m_SendData);
|
|
|
SendDeviceMsg(m_SendData);
|
|
|
}
|
|
|
|
|
|
void MsgHandler::ReadCard(const std::vector<uint8_t>& data)
|
|
|
{
|
|
|
std::vector<uint8_t> m_SendData;
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
|
|
|
|
m_SendData.insert(m_SendData.end(), {0x05, 0x01, 0x04, data[0]});
|
|
|
// ========== 新增:记录当前块操作开始时间 ==========
|
|
|
m_CurrentOperatingBlock = data[0];
|
|
|
m_BlockOpStartTime = std::chrono::steady_clock::now();
|
|
|
m_BlockOperationPending = true;
|
|
|
m_PendingCommand = PendingCommand::ReadBlock;
|
|
|
m_PendingBlock = data[0];
|
|
|
++m_OperationSequence;
|
|
|
|
|
|
|
|
|
CRC16(m_SendData);
|
|
|
SendDeviceMsg(m_SendData);
|
|
|
// 【新增】保护延时:给 RC522 和 STM32 固件留出处理时间
|
|
|
// 原因:连续发读块命令时,RC522 内部认证状态可能丢失
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
}
|
|
|
|
|
|
void MsgHandler::WriteCard(const std::vector<uint8_t>& data)
|
|
|
{
|
|
|
// ★ 在调串口之前先存请求参数(响应时要用)
|
|
|
// data = [blockNum(1)] + [16 字节块数据]
|
|
|
if (data.size() != 17U)
|
|
|
{
|
|
|
DEBUG("WriteCard rejected: expected block plus 16 data bytes, size=" << data.size());
|
|
|
m_BlockOperationPending = false;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_PendingWriteBlock = data[0];
|
|
|
m_PendingWriteValue.assign(data.begin() + 1, data.end());
|
|
|
|
|
|
DEBUG("WriteCard request block=" << static_cast<int>(m_PendingWriteBlock)
|
|
|
<< " data_size=" << m_PendingWriteValue.size()
|
|
|
<< " data=" << HexBytes(m_PendingWriteValue));
|
|
|
|
|
|
// ========== 新增:记录当前块操作开始时间 ==========
|
|
|
m_CurrentOperatingBlock = data[0];
|
|
|
m_BlockOpStartTime = std::chrono::steady_clock::now();
|
|
|
m_BlockOperationPending = true;
|
|
|
m_PendingCommand = PendingCommand::WriteBlock;
|
|
|
m_PendingBlock = data[0];
|
|
|
++m_OperationSequence;
|
|
|
|
|
|
|
|
|
std::vector<uint8_t> m_SendData;
|
|
|
m_SendData.insert(m_SendData.end(), {0x15, 0x01, 0x05});
|
|
|
m_SendData.insert(m_SendData.end(), data.begin(), data.end());
|
|
|
|
|
|
DEBUG("WriteCard frame_without_crc size=" << m_SendData.size()
|
|
|
<< " bytes=" << HexBytes(m_SendData));
|
|
|
|
|
|
CRC16(m_SendData);
|
|
|
|
|
|
DEBUG("WriteCard frame_with_crc size=" << m_SendData.size()
|
|
|
<< " bytes=" << HexBytes(m_SendData));
|
|
|
SendDeviceMsg(m_SendData);
|
|
|
}
|
|
|
|
|
|
void MsgHandler::BeepCard(const std::vector<uint8_t>& data)
|
|
|
{
|
|
|
std::vector<uint8_t> m_SendData;
|
|
|
m_PendingCommand = PendingCommand::Beep;
|
|
|
++m_OperationSequence;
|
|
|
|
|
|
uint8_t duration = data.empty() ? 0x0F : data[0];
|
|
|
m_SendData.insert(m_SendData.end(), {0x05, 0x01, 0x01, duration});
|
|
|
CRC16(m_SendData);
|
|
|
SendDeviceMsg(m_SendData);
|
|
|
}
|
|
|
|
|
|
void MsgHandler::ResetCard(void)
|
|
|
{
|
|
|
if (fd != -1)
|
|
|
{
|
|
|
tcflush(fd, TCIOFLUSH);
|
|
|
}
|
|
|
m_ReceiveBuffer.clear();
|
|
|
DEBUG("ResetCard: serial buffer flushed");
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 重置串口并重新初始化 RC522 ==========
|
|
|
// 用于块操作超时后的恢复,因为 RC522 可能在某些情况下完全无响应
|
|
|
bool MsgHandler::ResetSerialAndReinit()
|
|
|
{
|
|
|
DEBUG("ResetSerialAndReinit: resetting serial port and RC522");
|
|
|
m_ReceiveBuffer.clear();
|
|
|
|
|
|
// 第一步:关闭当前串口
|
|
|
ClosePort();
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
|
// 第二步:重新打开串口(使用保存的端口)
|
|
|
if (m_Port.empty())
|
|
|
{
|
|
|
DEBUG("ResetSerialAndReinit: m_Port is empty, cannot reopen");
|
|
|
return false;
|
|
|
}
|
|
|
std::string tty = "/dev/" + m_Port;
|
|
|
DEBUG("ResetSerialAndReinit: reopening " << tty << " with baudrate " << (int)m_Baudrate);
|
|
|
|
|
|
fd = open(tty.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
|
|
|
if (fd == -1)
|
|
|
{
|
|
|
DEBUG("ResetSerialAndReinit: failed to reopen serial port " << tty);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 第三步:重新配置串口参数(使用保存的波特率)
|
|
|
struct termios options;
|
|
|
if (tcgetattr(fd, &options) != 0)
|
|
|
{
|
|
|
DEBUG("ResetSerialAndReinit: failed to get serial attributes");
|
|
|
close(fd);
|
|
|
fd = -1;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
cfmakeraw(&options);
|
|
|
cfsetispeed(&options, m_Baudrate);
|
|
|
cfsetospeed(&options, m_Baudrate);
|
|
|
options.c_cflag |= CS8;
|
|
|
options.c_cflag &= ~PARENB;
|
|
|
options.c_cflag &= ~CSTOPB;
|
|
|
options.c_cflag &= ~CRTSCTS;
|
|
|
options.c_cc[VMIN] = 0;
|
|
|
options.c_cc[VTIME] = 10;
|
|
|
|
|
|
tcflush(fd, TCIOFLUSH);
|
|
|
if (tcsetattr(fd, TCSANOW, &options) != 0)
|
|
|
{
|
|
|
DEBUG("ResetSerialAndReinit: failed to set serial attributes");
|
|
|
close(fd);
|
|
|
fd = -1;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
DEBUG("ResetSerialAndReinit: serial port reopened successfully");
|
|
|
|
|
|
// 第四步:等待 RC522 准备好
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
|
// 第五步:清空 RC522 内部状态(直接发命令,不等待响应)
|
|
|
StopCard();
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
|
// 第六步:重新下载所有扇区密码(直接发命令,不等待响应)
|
|
|
for (auto& kv : m_PassWord) {
|
|
|
SetPasswd({kv.first});
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
}
|
|
|
|
|
|
// 第七步:启动寻卡(直接发命令,不等待响应)
|
|
|
FindCard();
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
|
DEBUG("ResetSerialAndReinit: commands sent, waiting for RC522 to recover...");
|
|
|
|
|
|
DEBUG("ResetSerialAndReinit: RC522 reinitialized successfully");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MsgHandler::WaitForResponse(uint8_t expected_cmd, int timeout_ms)
|
|
|
{
|
|
|
int elapsed = 0;
|
|
|
const int poll_interval = 100;
|
|
|
|
|
|
while (elapsed < timeout_ms)
|
|
|
{
|
|
|
std::vector<uint8_t> data;
|
|
|
if (RecvDeviceMsg(data, poll_interval) > 0)
|
|
|
{
|
|
|
if (data.size() >= 4 && data[2] == expected_cmd && data[3] == 0x00)
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
elapsed += poll_interval;
|
|
|
}
|
|
|
|
|
|
DEBUG("WaitForResponse timeout, expected_cmd=0x"
|
|
|
<< std::hex << (int)expected_cmd);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::InitSequence(void)
|
|
|
{
|
|
|
// 第一步:停止寻卡
|
|
|
StopCard();
|
|
|
if (!WaitForResponse(STOP_CARD))
|
|
|
{
|
|
|
DEBUG("InitSequence: StopCard failed");
|
|
|
return false;
|
|
|
}
|
|
|
DEBUG("InitSequence: StopCard OK");
|
|
|
|
|
|
// 第二步:逐扇区下载密码(升序)
|
|
|
for (auto& kv : m_PassWord)
|
|
|
{
|
|
|
SetPasswd({kv.first});
|
|
|
if (!WaitForResponse(SET_PWD))
|
|
|
{
|
|
|
DEBUG("InitSequence: SetPasswd sector "
|
|
|
<< (int)kv.first << " failed");
|
|
|
return false;
|
|
|
}
|
|
|
DEBUG("InitSequence: SetPasswd sector "
|
|
|
<< (int)kv.first << " OK");
|
|
|
}
|
|
|
|
|
|
// 第三步:启动寻卡
|
|
|
FindCard();
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 5000; // 发送后5000ms超时保护
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
DEBUG("InitSequence: complete, auto-find enabled");
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::AutoFindTick(void)
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
|
|
|
if (!m_AutoFind)
|
|
|
return;
|
|
|
|
|
|
if (m_CardState != CardState::AUTO_FIND)
|
|
|
return;
|
|
|
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
now - m_LastFindTime).count();
|
|
|
|
|
|
if (elapsed >= m_FindIntervalMs)
|
|
|
{
|
|
|
DEBUG("AUTO_FIND tick elapsed_ms=" << elapsed
|
|
|
<< " interval_ms=" << m_FindIntervalMs);
|
|
|
FindCard();
|
|
|
m_FindIntervalMs = 5000; // 发送后5000ms超时保护
|
|
|
m_LastFindTime = now;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MsgHandler::SetCardState(CardState state)
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
const CardState oldState = m_CardState;
|
|
|
m_CardState = state;
|
|
|
m_StateEnterTime = std::chrono::steady_clock::now();
|
|
|
DEBUG("STATE " << CardStateName(oldState) << " -> " << CardStateName(state));
|
|
|
}
|
|
|
|
|
|
unsigned long MsgHandler::GetCurrentIndex() const
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
return m_CurrentIndex;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::IsTimeoutRecovery() const
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
return m_IsInTimeoutRecovery;
|
|
|
}
|
|
|
|
|
|
uint64_t MsgHandler::GetOperationSequence() const
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
return m_OperationSequence;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::IsReadResponseReady() const
|
|
|
{
|
|
|
// 检查读卡响应是否准备好(块数据已累积完成)
|
|
|
return (m_CardState == CardState::WAIT_DDS_WRITE) && (m_BlockCount > 0);
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::IsWriteResponseReady() const
|
|
|
{
|
|
|
// 写卡响应在每次写成功后立即可用
|
|
|
return false; // 由 ParseDeviceMsg 实时处理
|
|
|
}
|
|
|
|
|
|
void MsgHandler::ClearReadBuffer()
|
|
|
{
|
|
|
m_BlockData.clear();
|
|
|
m_BlockCount = 0;
|
|
|
m_ResultCode = 0;
|
|
|
m_RequestedBlocks.clear(); // 【新增】清空块号列表
|
|
|
m_SkippedBlocks.clear(); // 【新增】清空跳过的块记录
|
|
|
}
|
|
|
|
|
|
void MsgHandler::SetReadResult(uint8_t resultCode, const std::string& cardId,
|
|
|
uint8_t blockCount,
|
|
|
const std::vector<std::pair<uint8_t, std::vector<uint8_t>>>& blockData)
|
|
|
{
|
|
|
m_ResultCode = resultCode;
|
|
|
m_CardId = cardId;
|
|
|
m_BlockCount = blockCount;
|
|
|
m_BlockData = blockData;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::SetWriteResult(uint8_t resultCode, const std::string& cardId, uint8_t blockNum)
|
|
|
{
|
|
|
m_ResultCode = resultCode;
|
|
|
m_CardId = cardId;
|
|
|
// blockNum 已在写卡命令中处理
|
|
|
}
|
|
|
|
|
|
int MsgHandler::CheckDdsTimeout()
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
|
|
|
// 设备读写事务期间不使用空闲超时,避免中断块序列。
|
|
|
if (m_CardState == CardState::WAIT_DDS_READ)
|
|
|
{
|
|
|
if (m_BlockOperationPending || m_PendingCommand != PendingCommand::None)
|
|
|
return 0;
|
|
|
|
|
|
const auto now = std::chrono::steady_clock::now();
|
|
|
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
now - m_StateEnterTime).count();
|
|
|
if (elapsed >= CARD_READY_IDLE_TIMEOUT_MS)
|
|
|
{
|
|
|
DEBUG("Card ready idle timeout (" << CARD_READY_IDLE_TIMEOUT_MS
|
|
|
<< "ms), reverting to AUTO_FIND");
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = now;
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
return 1;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
// 【新增】WAIT_DDS_WRITE 写卡块操作超时:等 RC522 响应超过 2s 视为失败,回 AUTO_FIND
|
|
|
if (m_CardState == CardState::WAIT_DDS_WRITE)
|
|
|
{
|
|
|
if (!m_BlockOperationPending)
|
|
|
return 0;
|
|
|
const auto writeNow = std::chrono::steady_clock::now();
|
|
|
const auto writeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
writeNow - m_BlockOpStartTime).count();
|
|
|
if (writeElapsed >= WRITE_BLOCK_OPERATION_TIMEOUT_MS)
|
|
|
{
|
|
|
DEBUG("Write block operation timeout (" << WRITE_BLOCK_OPERATION_TIMEOUT_MS
|
|
|
<< "ms), reverting to AUTO_FIND");
|
|
|
// 清理 pending 标志
|
|
|
m_BlockOperationPending = false;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
// 清串口残留帧(避免下个循环再拿到那 5 字节尾巴)
|
|
|
m_ReceiveBuffer.clear();
|
|
|
if (fd != -1)
|
|
|
tcflush(fd, TCIFLUSH);
|
|
|
// 恢复自动寻卡
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 5000;
|
|
|
//m_LastFindTime = writeNow;
|
|
|
m_LastFindTime = writeNow - std::chrono::milliseconds(m_FindIntervalMs);
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
return 1;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (m_CardState != CardState::WAIT_DDS_DECISION
|
|
|
&& m_CardState != CardState::WAIT_DDS_WRITE_NEXT)
|
|
|
return 0;
|
|
|
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
now - m_StateEnterTime).count();
|
|
|
|
|
|
// ★ 不同状态用不同的超时阈值
|
|
|
int timeout_ms = DDS_TIMEOUT_MS;
|
|
|
if (m_CardState == CardState::WAIT_DDS_DECISION)
|
|
|
{
|
|
|
timeout_ms = DDS_DECISION_TIMEOUT_MS;
|
|
|
}
|
|
|
|
|
|
else if (m_CardState == CardState::WAIT_DDS_WRITE_NEXT) // 【新增】
|
|
|
{
|
|
|
timeout_ms = DDS_WRITE_TIMEOUT_MS; // 【新增】10秒
|
|
|
}
|
|
|
|
|
|
if (elapsed >= timeout_ms)
|
|
|
{
|
|
|
DEBUG("DDS timeout (" << timeout_ms << "ms), reverting to AUTO_FIND");
|
|
|
|
|
|
// 恢复自动寻卡
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 5000;
|
|
|
m_LastFindTime = now;
|
|
|
|
|
|
// 清空读卡缓冲区
|
|
|
ClearReadBuffer();
|
|
|
|
|
|
// 切换状态
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
return 1; // 返回1表示触发了超时
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::ResetWriteTimeout()
|
|
|
{
|
|
|
if (m_CardState == CardState::WAIT_DDS_WRITE_NEXT)
|
|
|
m_StateEnterTime = std::chrono::steady_clock::now();
|
|
|
}
|
|
|
|
|
|
// ========== 单块操作超时控制实现 ==========
|
|
|
|
|
|
int MsgHandler::GetBlockOpElapsed() const
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
now - m_BlockOpStartTime).count();
|
|
|
}
|
|
|
|
|
|
uint8_t MsgHandler::GetCurrentOperatingBlock() const
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
return m_CurrentOperatingBlock;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::IsBlockOperationPending() const
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
return m_BlockOperationPending;
|
|
|
}
|
|
|
|
|
|
bool MsgHandler::HasPendingReadResponse() const
|
|
|
{
|
|
|
return !m_PendingReadResponse.empty();
|
|
|
}
|
|
|
|
|
|
std::vector<uint8_t> MsgHandler::GetPendingReadResponse()
|
|
|
{
|
|
|
auto tmp = m_PendingReadResponse;
|
|
|
m_PendingReadResponse.clear();
|
|
|
return tmp;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::SkipCurrentBlock()
|
|
|
{
|
|
|
|
|
|
|
|
|
DEBUG("SkipCurrentBlock: state=" << (int)m_CardState
|
|
|
<< ", index=" << (int)m_CurrentReadIndex
|
|
|
<< ", total=" << (int)m_RequestedBlocks.size());
|
|
|
|
|
|
|
|
|
// 【新增】标记正在恢复,防止 HandleDdsMsg 在此期间发新命令
|
|
|
m_IsInTimeoutRecovery = true;
|
|
|
|
|
|
|
|
|
if (m_CardState != CardState::WAIT_DDS_READ)
|
|
|
{
|
|
|
DEBUG("SkipCurrentBlock: not in WAIT_DDS_READ state, ignored");
|
|
|
m_IsInTimeoutRecovery = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (m_RequestedBlocks.empty() || m_CurrentReadIndex >= m_RequestedBlocks.size())
|
|
|
{
|
|
|
DEBUG("SkipCurrentBlock: no more blocks to skip");
|
|
|
m_IsInTimeoutRecovery = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
uint8_t skipBlock = m_RequestedBlocks[m_CurrentReadIndex];
|
|
|
DEBUG("Read block timeout, block=" << static_cast<int>(skipBlock));
|
|
|
|
|
|
// 超时不是成功数据,使用 README 约定的设备超时错误码 0x0015。
|
|
|
std::vector<uint8_t> rsp_data;
|
|
|
rsp_data.push_back(1);
|
|
|
// README 约定:设备超时使用大端结果码 0x0015。
|
|
|
rsp_data.push_back(0x00);
|
|
|
rsp_data.push_back(0x15);
|
|
|
rsp_data.push_back(static_cast<uint8_t>(m_CardId.size()));
|
|
|
rsp_data.insert(rsp_data.end(), m_CardId.begin(), m_CardId.end());
|
|
|
rsp_data.push_back(0);
|
|
|
|
|
|
m_PendingReadResponse = rsp_data;
|
|
|
ClearReadBuffer();
|
|
|
m_CurrentOperatingBlock = 0;
|
|
|
m_BlockOperationPending = false;
|
|
|
m_IsInTimeoutRecovery = false;
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
DEBUG("Read block timeout response ready, block=" << static_cast<int>(skipBlock));
|
|
|
return;
|
|
|
|
|
|
|
|
|
}
|
|
|
CardState MsgHandler::GetCardState() const
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
return m_CardState;
|
|
|
}
|
|
|
|
|
|
void MsgHandler::ForceResetToAutoFind()
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
DEBUG("ForceResetToAutoFind: reverting to AUTO_FIND");
|
|
|
m_AutoFind = true;
|
|
|
m_FindIntervalMs = 500;
|
|
|
m_LastFindTime = std::chrono::steady_clock::now();
|
|
|
m_BlockOperationPending = false;
|
|
|
m_PendingCommand = PendingCommand::None;
|
|
|
m_CurrentOperatingBlock = 0;
|
|
|
m_ReadBlockRetryCount = 0;
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
}
|
|
|
|
|
|
void MsgHandler::PauseAutoFind()
|
|
|
{
|
|
|
std::lock_guard<std::recursive_mutex> lock(mtx_);
|
|
|
DEBUG("PauseAutoFind: state=AUTO_FIND, m_AutoFind=false (waiting after QueueTrigger)");
|
|
|
m_AutoFind = false;
|
|
|
SetCardState(CardState::AUTO_FIND);
|
|
|
}
|
|
|
|
|
|
// ========== refreshPort 默认实现 ==========
|
|
|
bool MsgHandler::refreshPort()
|
|
|
{
|
|
|
DEBUG("MsgHandler::refreshPort: base class default implementation, no-op");
|
|
|
return true;
|
|
|
}
|