| | |
| | | #define LOG_DEBUG(msg) |
| | | #endif |
| | | |
| | | // 初始化静态成员变量 |
| | | // 初始化静态成员变量 |
| | | std::unordered_map<int, std::string> CPerformanceMelsec::m_mapError = { |
| | | // 板块SDK错误码 |
| | | // 板块SDK错误码 |
| | | {0, "No error, communication successful."}, |
| | | {1, "Driver not started. The driver is not running."}, |
| | | {2, "Timeout error (board response error). Request not completed within timeout."}, |
| | |
| | | {-28634, "Hardware self-diagnosis error."}, |
| | | {-28636, "Hardware self-diagnosis error."}, |
| | | |
| | | // 自定义错误码 |
| | | // 自定义错误码 |
| | | {ERROR_CODE_UNKNOWN, "Error: Unknown error code."}, |
| | | {ERROR_CODE_NOT_CONNECTED, "Error: Not connected to the device."}, |
| | | {ERROR_CODE_INVALID_PARAM, "Error: Invalid parameter."}, |
| | |
| | | m_bConnected.store(false); |
| | | } |
| | | |
| | | // 析构函数 |
| | | // 析构函数 |
| | | CPerformanceMelsec::~CPerformanceMelsec() { |
| | | Disconnect(); |
| | | } |
| | | |
| | | // 获取最近的错误信息 |
| | | // 获取最近的错误信息 |
| | | std::string CPerformanceMelsec::GetLastError() const { |
| | | return m_strLastError; |
| | | } |
| | | |
| | | // 保存错误信息 |
| | | // 保存错误信息 |
| | | bool CPerformanceMelsec::SaveErrorInfoToFile(const std::string& filename) { |
| | | // 打开文件 |
| | | // 打开文件 |
| | | std::ofstream file(filename); |
| | | if (!file.is_open()) { |
| | | std::cerr << "Failed to open file for saving: " << filename << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | // 遍历静态成员变量 m_mapError 并将每个错误信息写入文件 |
| | | // 遍历静态成员变量 m_mapError 并将每个错误信息写入文件 |
| | | for (const auto& entry : m_mapError) { |
| | | const int nCode = entry.first; |
| | | const std::string& strMessage = entry.second; |
| | |
| | | return true; |
| | | } |
| | | |
| | | // 加载错误信息 |
| | | // 加载错误信息 |
| | | bool CPerformanceMelsec::LoadErrorInfoFromFile(const std::string& filename) { |
| | | std::ifstream inFile(filename); |
| | | if (!inFile.is_open()) { |
| | |
| | | std::string strToken; |
| | | std::string strMessage; |
| | | |
| | | // 使用分隔符 "|" 解析每一行 |
| | | // 使用分隔符 "|" 解析每一行 |
| | | if (std::getline(iss, strToken, '|')) { |
| | | nCode = std::stoi(strToken); |
| | | } |
| | |
| | | return true; |
| | | } |
| | | |
| | | // 连接到PLC |
| | | // 连接到PLC |
| | | int CPerformanceMelsec::Connect(const short nChannel, const short nMode) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | |
| | |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | // 连接PLC,显式类型转换以匹配 mdOpen 的签名 |
| | | // 连接PLC,显式类型转换以匹配 mdOpen 的签名 |
| | | const short nRet = mdOpen(nChannel, nMode, &m_nPath); |
| | | if (nRet == 0) { |
| | | m_bConnected.store(true); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 断开连接 |
| | | // 断开连接 |
| | | int CPerformanceMelsec::Disconnect() { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 可编程控制器软元件信息表的初始化 |
| | | // 可编程控制器软元件信息表的初始化 |
| | | int CPerformanceMelsec::InitializeController() { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 获取版本信息 |
| | | // 获取版本信息 |
| | | int CPerformanceMelsec::GetBoardVersion(BoardVersion& version) { |
| | | if (!m_bConnected.load()) { |
| | | UpdateLastError(ERROR_CODE_NOT_CONNECTED); |
| | | return ERROR_CODE_NOT_CONNECTED; |
| | | } |
| | | |
| | | // 获取版本信息 |
| | | // 获取版本信息 |
| | | short buf[32] = { 0 }; |
| | | const short nRet = mdBdVerRead(m_nPath, buf); |
| | | if (nRet != 0) { |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 填充版本信息到结构体 |
| | | // 填充版本信息到结构体 |
| | | version.fixedValue[0] = static_cast<char>(buf[0] & 0xFF); |
| | | version.fixedValue[1] = static_cast<char>((buf[0] >> 8) & 0xFF); |
| | | |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 读取目标站点CPU类型 |
| | | // 读取目标站点CPU类型 |
| | | int CPerformanceMelsec::ReadCPUCode(const StationIdentifier& station, short& nCPUCode) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 确保线程安全的最小锁定范围 |
| | | // 确保线程安全的最小锁定范围 |
| | | { |
| | | nCPUCode = 0; |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 板模式设置 |
| | | // 板模式设置 |
| | | int CPerformanceMelsec::SetBoardMode(const short nMode) { |
| | | // 检查是否已经连接 |
| | | // 检查是否已经连接 |
| | | if (!m_bConnected.load()) { |
| | | UpdateLastError(ERROR_CODE_NOT_CONNECTED); |
| | | return ERROR_CODE_NOT_CONNECTED; |
| | | } |
| | | |
| | | // 确保线程安全的最小锁定范围 |
| | | // 确保线程安全的最小锁定范围 |
| | | short nRet = 0; |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 获取板模式 |
| | | // 获取板模式 |
| | | int CPerformanceMelsec::GetBoardMode(short& nMode) { |
| | | // 检查是否已经连接 |
| | | // 检查是否已经连接 |
| | | if (!m_bConnected.load()) { |
| | | UpdateLastError(ERROR_CODE_NOT_CONNECTED); |
| | | return ERROR_CODE_NOT_CONNECTED; |
| | |
| | | return 0; |
| | | } |
| | | |
| | | // 板复位 |
| | | // 板复位 |
| | | int CPerformanceMelsec::BoardReset() { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | if (!m_bConnected.load()) { |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 板LED读取 |
| | | // 板LED读取 |
| | | int CPerformanceMelsec::ReadBoardLed(std::vector<short>& vecLedBuffer) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | if (!m_bConnected.load()) { |
| | |
| | | return ERROR_CODE_NOT_CONNECTED; |
| | | } |
| | | |
| | | // 清空 LED 缓冲区 |
| | | // 清空 LED 缓冲区 |
| | | vecLedBuffer.clear(); |
| | | vecLedBuffer.resize(16, 0); |
| | | |
| | | // 调用 SDK 函数读取 LED 数据 |
| | | // 调用 SDK 函数读取 LED 数据 |
| | | const short nRet = mdBdLedRead(m_nPath, vecLedBuffer.data()); |
| | | if (nRet != 0) { |
| | | UpdateLastError(ERROR_CODE_NOT_CONNECTED); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 获取板状态 |
| | | // 获取板状态 |
| | | int CPerformanceMelsec::GetBoardStatus(BoardStatus& status) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | if (!m_bConnected) { |
| | |
| | | LOG_ERROR(m_strLastError); |
| | | } |
| | | |
| | | // 将 buf 映射到结构体 |
| | | // 将 buf 映射到结构体 |
| | | status = BoardStatus::fromBuffer(buf); |
| | | return 0; |
| | | } |
| | | |
| | | // 通用读数据 |
| | | // 通用读数据 |
| | | int CPerformanceMelsec::ReadData(const StationIdentifier& station, const short nDevType, const short nDevNo, short nSize, std::vector<short>& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStationAndSize(station, nSize); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 初始化读取缓冲区 |
| | | // 初始化读取缓冲区 |
| | | vecData.clear(); |
| | | vecData.resize(nSize, 0); |
| | | |
| | | // 确保线程安全的最小锁定范围 |
| | | // 确保线程安全的最小锁定范围 |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | short* pData = vecData.data(); |
| | |
| | | } |
| | | |
| | | if (nRet != 0) { |
| | | vecData.clear(); // 如果读取失败,清空缓冲区 |
| | | vecData.clear(); // 如果读取失败,清空缓冲区 |
| | | } |
| | | |
| | | return nRet; |
| | | } |
| | | |
| | | // 读取位数据 |
| | | // 读取位数据 |
| | | int CPerformanceMelsec::ReadBitData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const short nBitCount, BitContainer& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStationAndSize(station, nBitCount); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 计算需要读取的字节大小(按位对齐为字节数) |
| | | if (nDevNo % 8 != 0) { |
| | | nRet = -2; |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | | const auto nSize = static_cast<short>((nBitCount + 7) / 8); // 向上取整 |
| | | std::vector<short> vecTempBuffer((nSize + 1) / 2, 0); // 临时缓冲区,字节对齐 |
| | | const auto nSize = static_cast<short>((static_cast<int>(nBitCount) + 15) / 16); // 计算需要读取的字数量(向上取整) |
| | | |
| | | std::vector<short> vecTempBuffer(nSize, 0); |
| | | nRet = ReadData(station, nDevType, nDevNo, nSize, vecTempBuffer); |
| | | |
| | | if (nRet == 0) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertShortToUint8(vecTempBuffer, vecData); |
| | | vecData.clear(); |
| | | |
| | | // 将字数据解析为位数据 |
| | | for (short nIdx = 0; nIdx < nSize; ++nIdx) { |
| | | const short nCurrentValue = vecTempBuffer[nIdx]; |
| | | // 遍历当前 short 中的每一位 |
| | | for (int bitIdx = 0; bitIdx < 16; ++bitIdx) { |
| | | bool bBit = (nCurrentValue & (1 << bitIdx)) != 0; |
| | | vecData.push_back(bBit); |
| | | if (vecData.size() >= nBitCount) { |
| | | return nRet; // 如果已经读取完所需的位数,提前退出 |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return nRet; |
| | | } |
| | | |
| | | // 读取字数据 |
| | | // 读取字数据 |
| | | int CPerformanceMelsec::ReadWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const short nWordCount, WordContainer& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStationAndSize(station, nWordCount); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 读取双字数据 |
| | | // 读取双字数据 |
| | | int CPerformanceMelsec::ReadDWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const short nDWordCount, DWordContainer& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStationAndSize(station, nDWordCount); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | const auto nSize = static_cast<short>(nDWordCount * 2); // 每个双字占两个字(每个双字占 4 字节) |
| | | const auto nSize = static_cast<short>(nDWordCount * 2); // 每个双字占两个字(每个双字占 4 字节) |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | | std::vector<short> vecTempBuffer(nSize, 0); |
| | | nRet = ReadData(station, nDevType, nDevNo, nSize, vecTempBuffer); |
| | | |
| | | if (nRet == 0) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertShortToUint32(vecTempBuffer, vecData); |
| | | } |
| | | |
| | | return nRet; |
| | | } |
| | | |
| | | // 通用写数据 |
| | | // 通用写数据 |
| | | int CPerformanceMelsec::WriteData(const StationIdentifier& station, const short nDevType, const short nDevNo, short nSize, short* pData) { |
| | | // 验证站点参数 |
| | | // 验证站点参数 |
| | | int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 数据有效性 |
| | | // 数据有效性 |
| | | if (nSize < 0 || pData == nullptr) { |
| | | UpdateLastError(ERROR_CODE_INVALID_PARAM); |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | // 确保线程安全的最小锁定范围 |
| | | // 确保线程安全的最小锁定范围 |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | nSize *= sizeof(short); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 写位数据 |
| | | // 写位数据 |
| | | int CPerformanceMelsec::WriteBitData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const BitContainer& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | const int nRet = ValidateStationAndData(station, vecData); |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStationAndData(station, vecData); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 计算需要写入的字节数(位数据需要按 8 位对齐为字节数) |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | | const auto nSize = static_cast<short>((vecData.size() + 7) / 8); |
| | | std::vector<short> vecBuffer(vecData.size() / 2 + vecData.size() % 2, 0); |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertUint8ToShort(vecData, vecBuffer); |
| | | if (nDevNo % 8 != 0) { |
| | | nRet = -2; |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | return WriteData(station, nDevType, nDevNo, nSize, vecBuffer.data()); |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | | const auto nSize = static_cast<short>((static_cast<int>(vecData.size()) + 15) / 16); // 计算需要写入的字数量(向上取整) |
| | | |
| | | // 准备临时缓冲区来存储转换后的 16 位数据 |
| | | std::vector<short> vecTempBuffer(nSize, 0); |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | // 将位数据按字打包到临时缓冲区 |
| | | for (int i = 0; i < vecData.size(); ++i) { |
| | | if (vecData[i]) { |
| | | // 使用 & 0xFFFF 保证不会超过 16 位,防止溢出 |
| | | vecTempBuffer[i / 16] |= static_cast<short>((1 << (i % 16)) & 0xFFFF); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return WriteData(station, nDevType, nDevNo, nSize, vecTempBuffer.data()); |
| | | } |
| | | |
| | | // 写字数据 |
| | | // 写字数据 |
| | | int CPerformanceMelsec::WriteWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const WordContainer& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | const int nRet = ValidateStationAndData(station, vecData); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 计算需要写入的字节数(每个字占 2 字节) |
| | | // 计算需要写入的字节数(每个字占 2 字节) |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | | const auto nSize = static_cast<short>(vecData.size()); |
| | | const auto pData = const_cast<short*>(reinterpret_cast<const short*>(vecData.data())); |
| | |
| | | return WriteData(station, nDevType, nDevNo, nSize, pData); |
| | | } |
| | | |
| | | // 写双字数据 |
| | | // 写双字数据 |
| | | int CPerformanceMelsec::WriteDWordData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const DWordContainer& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | const int nRet = ValidateStationAndData(station, vecData); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 计算需要写入的字节数(每个双字占 4 字节) |
| | | // 计算需要写入的字节数(每个双字占 4 字节) |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | | const auto nSize = static_cast<short>(vecData.size() * sizeof(short)); |
| | | std::vector<short> vecBuffer(nSize, 0); |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertUint32ToShort(vecData, vecBuffer); |
| | | } |
| | | |
| | | return WriteData(station, nDevType, nDevNo, nSize, vecBuffer.data()); |
| | | } |
| | | |
| | | // 扩展读数据 |
| | | // 扩展读数据 |
| | | long CPerformanceMelsec::ReadDataEx(const StationIdentifier& station, long nDevType, long nDevNo, long nSize, std::vector<char>& vecData) { |
| | | // 验证站点参数和读取大小是否有效 |
| | | // 验证站点参数和读取大小是否有效 |
| | | long nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | |
| | | std::vector<short> vecBuffer(nSize / 2, 0); |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | nRet = mdReceiveEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo, &nSize, vecBuffer.data()); |
| | | } |
| | | |
| | |
| | | LOG_ERROR(m_strLastError); |
| | | } |
| | | else { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | vecData.resize(nSize); |
| | | ConvertShortToChar(vecBuffer, vecData); |
| | | } |
| | |
| | | return 0; |
| | | } |
| | | |
| | | // 扩展写数据 |
| | | // 扩展写数据 |
| | | long CPerformanceMelsec::WriteDataEx(const StationIdentifier& station, long nDevType, long nDevNo, const std::vector<char>& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | long nRet = ValidateStationAndData(station, vecData); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 将 vecData 转换为 short 类型的缓冲区 |
| | | // 将 vecData 转换为 short 类型的缓冲区 |
| | | long nSize = static_cast<long>(vecData.size()); |
| | | nSize = nSize % 2 != 0 ? nSize + 1 : nSize; |
| | | std::vector<short> vecBuffer(nSize / 2, 0); |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertCharToShort(vecData, vecBuffer); |
| | | nRet = mdSendEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo, &nSize, vecBuffer.data()); |
| | | } |
| | | |
| | | // 错误处理和日志记录 |
| | | // 错误处理和日志记录 |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | LOG_ERROR(m_strLastError); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 扩展软元件随机读取 |
| | | // 扩展软元件随机读取 |
| | | long CPerformanceMelsec::ReadRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, std::vector<char>& vecData) { |
| | | if (vecSoftElements.empty()) { |
| | | UpdateLastError(ERROR_INVALID_PARAMETER); |
| | |
| | | return ERROR_INVALID_PARAMETER; |
| | | } |
| | | |
| | | // 准备 dev 数据 |
| | | std::vector<short> devBuffer(vecSoftElements.size() * 3 + 1, 0); // 每个软元件需要 3 个 short,外加一个计数器 |
| | | devBuffer[0] = static_cast<short>(vecSoftElements.size()); // 第一个元素是软元件数量 |
| | | // 准备 dev 数据 |
| | | std::vector<short> devBuffer(vecSoftElements.size() * 3 + 1, 0); // 每个软元件需要 3 个 short,外加一个计数器 |
| | | devBuffer[0] = static_cast<short>(vecSoftElements.size()); // 第一个元素是软元件数量 |
| | | for (size_t i = 0; i < vecSoftElements.size(); ++i) { |
| | | const SoftElement& element = vecSoftElements[i]; |
| | | devBuffer[i * 3 + 1] = element.nType; // 软元件类型 |
| | | devBuffer[i * 3 + 2] = static_cast<short>(element.nStartNo); // 起始软元件编号 |
| | | devBuffer[i * 3 + 3] = element.nElementCount; // 点数 |
| | | devBuffer[i * 3 + 1] = element.nType; // 软元件类型 |
| | | devBuffer[i * 3 + 2] = static_cast<short>(element.nStartNo); // 起始软元件编号 |
| | | devBuffer[i * 3 + 3] = element.nElementCount; // 点数 |
| | | } |
| | | |
| | | // 计算读取数据所需缓冲区大小 |
| | | // 计算读取数据所需缓冲区大小 |
| | | long nBufferSize = 0; |
| | | for (const auto& element : vecSoftElements) { |
| | | nBufferSize += element.nElementCount * 2; // 每个点占用 2 个字节 |
| | | nBufferSize += element.nElementCount * 2; // 每个点占用 2 个字节 |
| | | } |
| | | |
| | | // 锁保护及调用 mdRandREx |
| | | // 锁保护及调用 mdRandREx |
| | | long nRet = 0; |
| | | std::vector<short> vecBuffer(nBufferSize / 2, 0); |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 确保线程安全 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 确保线程安全 |
| | | nRet = mdRandREx(m_nPath, station.nNetNo, station.nStNo, devBuffer.data(), vecBuffer.data(), nBufferSize); |
| | | } |
| | | |
| | | // 错误处理和日志记录 |
| | | // 错误处理和日志记录 |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | LOG_ERROR(m_strLastError); |
| | | return nRet; |
| | | } |
| | | |
| | | // 将读取到的 short 数据转换为 char 数据 |
| | | // 将读取到的 short 数据转换为 char 数据 |
| | | vecData.resize(nBufferSize); |
| | | for (size_t i = 0; i < vecBuffer.size(); ++i) { |
| | | vecData[i * 2] = static_cast<char>(vecBuffer[i] & 0xFF); // 低字节 |
| | | vecData[i * 2 + 1] = static_cast<char>((vecBuffer[i] >> 8) & 0xFF); // 高字节 |
| | | vecData[i * 2] = static_cast<char>(vecBuffer[i] & 0xFF); // 低字节 |
| | | vecData[i * 2 + 1] = static_cast<char>((vecBuffer[i] >> 8) & 0xFF); // 高字节 |
| | | } |
| | | |
| | | return nRet; |
| | | } |
| | | |
| | | // 扩展软元件随机写入(支持多个软元件) |
| | | // 扩展软元件随机写入(支持多个软元件) |
| | | long CPerformanceMelsec::WriteRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, const std::vector<char>& vecData) { |
| | | if (vecSoftElements.empty() || vecData.empty()) { |
| | | UpdateLastError(ERROR_INVALID_PARAMETER); |
| | |
| | | return ERROR_INVALID_PARAMETER; |
| | | } |
| | | |
| | | // 准备 dev 数据 |
| | | std::vector<long> devBuffer(vecSoftElements.size() * 3 + 1, 0); // 每个软元件需要 3 个 long,外加一个计数器 |
| | | devBuffer[0] = static_cast<long>(vecSoftElements.size()); // 第一个元素是软元件数量 |
| | | // 准备 dev 数据 |
| | | std::vector<long> devBuffer(vecSoftElements.size() * 3 + 1, 0); // 每个软元件需要 3 个 long,外加一个计数器 |
| | | devBuffer[0] = static_cast<long>(vecSoftElements.size()); // 第一个元素是软元件数量 |
| | | for (size_t i = 0; i < vecSoftElements.size(); ++i) { |
| | | const SoftElement& element = vecSoftElements[i]; |
| | | devBuffer[i * 3 + 1] = static_cast<long>(element.nType); // 软元件类型 |
| | | devBuffer[i * 3 + 2] = element.nStartNo; // 起始软元件编号(已经是 long 类型,无需转换) |
| | | devBuffer[i * 3 + 3] = static_cast<long>(element.nElementCount); // 点数 |
| | | devBuffer[i * 3 + 1] = static_cast<long>(element.nType); // 软元件类型 |
| | | devBuffer[i * 3 + 2] = element.nStartNo; // 起始软元件编号(已经是 long 类型,无需转换) |
| | | devBuffer[i * 3 + 3] = static_cast<long>(element.nElementCount); // 点数 |
| | | } |
| | | |
| | | // 锁保护及调用 mdRandWEx |
| | | // 锁保护及调用 mdRandWEx |
| | | long nRet = 0; |
| | | std::vector<short> vecBuffer(vecData.size() / 2, 0); |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 确保线程安全 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 确保线程安全 |
| | | ConvertCharToShort(vecData, vecBuffer); |
| | | nRet = mdRandWEx(m_nPath, station.nNetNo, station.nStNo, devBuffer.data(), vecBuffer.data(), static_cast<long>(vecBuffer.size())); |
| | | } |
| | | |
| | | // 错误处理和日志记录 |
| | | // 错误处理和日志记录 |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | LOG_ERROR(m_strLastError); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 远程设备站/远程站的缓冲存储器读取 |
| | | // 远程设备站/远程站的缓冲存储器读取 |
| | | long CPerformanceMelsec::ReadRemoteBuffer(const StationIdentifier& station, long nOffset, long nSize, std::vector<char>& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | |
| | | long nActualSize = (nSize + 1) / 2; |
| | | std::vector<short> vecBuffer(nActualSize, 0); |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | nRet = mdRemBufReadEx(m_nPath, station.nNetNo, station.nStNo, nOffset, &nActualSize, vecBuffer.data()); |
| | | } |
| | | |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); // 更新错误码 |
| | | UpdateLastError(nRet); // 更新错误码 |
| | | LOG_ERROR(m_strLastError); |
| | | } |
| | | else { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertShortToChar(vecBuffer, vecData); |
| | | } |
| | | |
| | | return nRet; |
| | | } |
| | | |
| | | // 远程设备站/远程站的缓冲存储器写入 |
| | | // 远程设备站/远程站的缓冲存储器写入 |
| | | long CPerformanceMelsec::WriteRemoteBuffer(const StationIdentifier& station, long nOffset, const std::vector<char>& vecData) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | long nRet = ValidateStationAndData(station, vecData); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 将 vecData 转换为 short 类型的缓冲区 |
| | | // 将 vecData 转换为 short 类型的缓冲区 |
| | | long nSize = static_cast<long>(vecData.size()); |
| | | std::vector<short> vecBuffer((nSize + 1) / 2, 0); |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertCharToShort(vecData, vecBuffer); |
| | | nRet = mdRemBufWriteEx(m_nPath, station.nNetNo, station.nStNo, nOffset, &nSize, vecBuffer.data()); |
| | | } |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 远程站的缓冲存储器读取 对象站IP地址指定 |
| | | // 远程站的缓冲存储器读取 对象站IP地址指定 |
| | | long CPerformanceMelsec::ReadRemoteBufferByIp(const std::string& strIP, long nOffset, long nSize, std::vector<char>& vecData) { |
| | | uint32_t nAddress = 0; |
| | | if (nSize < 0 || !ConvertIpStringToUint32(strIP, nAddress)) { |
| | |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | // 将缓冲区大小调整为 nSize |
| | | // 将缓冲区大小调整为 nSize |
| | | vecData.resize(nSize, 0); |
| | | std::vector<short> vecBuffer((nSize + 1) / 2, 0); // 转换为 short 类型 |
| | | std::vector<short> vecBuffer((nSize + 1) / 2, 0); // 转换为 short 类型 |
| | | |
| | | // 调用底层 SDK |
| | | // 调用底层 SDK |
| | | long nRet = 0; |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | nRet = mdRemBufReadIPEx(m_nPath, static_cast<long>(nAddress), nOffset, &nSize, vecBuffer.data()); |
| | | } |
| | | |
| | |
| | | LOG_ERROR(m_strLastError); |
| | | } |
| | | else { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 |
| | | ConvertShortToChar(vecBuffer, vecData); |
| | | } |
| | | |
| | | return nRet; |
| | | } |
| | | |
| | | // 远程站的缓冲存储器写入 对象站IP地址指定 |
| | | // 远程站的缓冲存储器写入 对象站IP地址指定 |
| | | long CPerformanceMelsec::WriteRemoteBufferByIp(const std::string& strIP, long nOffset, const std::vector<char>& vecData) { |
| | | uint32_t nAddress = 0; |
| | | if (vecData.empty() || !ConvertIpStringToUint32(strIP, nAddress)) { |
| | |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | // 转换 vecData 为 short 类型的缓冲区 |
| | | // 转换 vecData 为 short 类型的缓冲区 |
| | | long nSize = static_cast<long>(vecData.size()); |
| | | std::vector<short> vecBuffer((nSize + 1) / 2, 0); |
| | | |
| | | long nRet = 0; |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全 |
| | | ConvertCharToShort(vecData, vecBuffer); |
| | | nRet = mdRemBufWriteIPEx(m_nPath, static_cast<long>(nAddress), nOffset, &nSize, vecBuffer.data()); |
| | | } |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 设置(ON)对象站的指定位软元件 |
| | | // 设置(ON)对象站的指定位软元件 |
| | | int CPerformanceMelsec::SetBitDevice(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo) { |
| | | // 验证站点参数 |
| | | // 验证站点参数 |
| | | int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 确保线程安全的最小锁定范围 |
| | | // 确保线程安全的最小锁定范围 |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全 |
| | | std::lock_guard<std::mutex> lock(m_mtx); // 线程安全 |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | | nRet = mdDevSet(m_nPath, CombineStation(station), nDevType, nDevNo); |
| | | } |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 复位(OFF)对象站的指定位软元件 |
| | | // 复位(OFF)对象站的指定位软元件 |
| | | int CPerformanceMelsec::ResetBitDevice(const StationIdentifier& station, const DeviceType enDevType, const short enDevNo) { |
| | | // 验证站点参数 |
| | | // 验证站点参数 |
| | | int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 确保线程安全的最小锁定范围 |
| | | // 确保线程安全的最小锁定范围 |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | const short nDevType = CalculateDeviceType(station, enDevType); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 扩展位软元件设置 |
| | | // 扩展位软元件设置 |
| | | long CPerformanceMelsec::SetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | |
| | | // 检查参数有效性 |
| | | // 检查参数有效性 |
| | | long nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 扩展位软元件复位 |
| | | // 扩展位软元件复位 |
| | | long CPerformanceMelsec::ResetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | |
| | | // 检查参数有效性 |
| | | // 检查参数有效性 |
| | | long nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 执行对象站的CPU |
| | | // 执行对象站的CPU |
| | | int CPerformanceMelsec::ControlCPU(const StationIdentifier& station, ControlCode enControlCode) { |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | UpdateLastError(nRet); |
| | | return nRet; |
| | | } |
| | | |
| | | // 验证控制码是否合法 |
| | | // 验证控制码是否合法 |
| | | const auto nControlCode = static_cast<short>(enControlCode); |
| | | if (nControlCode < 0 || nControlCode > 2) { |
| | | UpdateLastError(ERROR_CODE_INVALID_PARAM); // 参数错误 |
| | | UpdateLastError(ERROR_CODE_INVALID_PARAM); // 参数错误 |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | // 确保线程安全的最小锁定范围 |
| | | // 确保线程安全的最小锁定范围 |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | nRet = mdControl(m_nPath, CombineStation(station), nControlCode); |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | // 事件等待 |
| | | // 事件等待 |
| | | int CPerformanceMelsec::WaitForBoardEvent(std::vector<short> vecEventNumbers, const int nTimeoutMs, EventDetails& details) { |
| | | std::lock_guard<std::mutex> lock(m_mtx); |
| | | |
| | |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | // 第 0 个元素存储数量,最大支持 64 个事件 |
| | | // 第 0 个元素存储数量,最大支持 64 个事件 |
| | | std::array<short, 65> eventno = { 0 }; |
| | | eventno[0] = static_cast<short>(vecEventNumbers.size()); |
| | | std::copy(vecEventNumbers.begin(), vecEventNumbers.end(), eventno.begin() + 1); |
| | | |
| | | // 初始化输出参数 |
| | | // 初始化输出参数 |
| | | details.nEventNo = 0; |
| | | details.details.fill(0); |
| | | |
| | |
| | | return nRet; |
| | | } |
| | | |
| | | //============================================辅助函数======================================================= |
| | | // 更新最近的错误信息 |
| | | //============================================辅助函数======================================================= |
| | | // 更新最近的错误信息 |
| | | void CPerformanceMelsec::UpdateLastError(const int nCode) { |
| | | if (nCode == 0) { |
| | | return; |
| | | } |
| | | |
| | | // 检查错误码是否存在于映射表中 |
| | | // 检查错误码是否存在于映射表中 |
| | | const auto it = m_mapError.find(nCode); |
| | | if (it != m_mapError.end()) { |
| | | // 如果找到,直接返回对应语言的错误信息 |
| | | // 如果找到,直接返回对应语言的错误信息 |
| | | m_strLastError = it->second; |
| | | } |
| | | else { |
| | | // 如果未找到,处理特殊范围 |
| | | // 如果未找到,处理特殊范围 |
| | | m_strLastError = "Unknown error."; |
| | | if (nCode == -28611 || nCode == -28612) { |
| | | // 系统出错 |
| | | // 系统出错 |
| | | m_strLastError = "System error."; |
| | | } |
| | | |
| | | if (nCode >= -20480 && nCode <= -16384) { |
| | | // CC-Link 系统检测出的错误 |
| | | // CC-Link 系统检测出的错误 |
| | | m_strLastError = "Error detected in the CC-Link system."; |
| | | } |
| | | |
| | | if (nCode >= -12288 && nCode <= -8193) { |
| | | // CC-Link IE TSN 系统检测出的错误 |
| | | // CC-Link IE TSN 系统检测出的错误 |
| | | m_strLastError = "Error detected in the CC-Link IE TSN system."; |
| | | } |
| | | |
| | | if (nCode >= -8192 && nCode <= -4097) { |
| | | // CC-Link IE 控制网络系统检测出的错误 |
| | | // CC-Link IE 控制网络系统检测出的错误 |
| | | m_strLastError = "Error detected in the CC-Link IE control network system."; |
| | | } |
| | | |
| | | if (nCode >= -4096 && nCode <= -257) { |
| | | // MELSECNET/10 或 MELSECNET/网络系统错误范围 |
| | | // MELSECNET/10 或 MELSECNET/网络系统错误范围 |
| | | m_strLastError = "Errors detected in MELSECNET/10 or MELSECNET/network system."; |
| | | } |
| | | |
| | | if (nCode >= 4096 && nCode <= 16383) { |
| | | // MELSEC 数据链接库范围 |
| | | // MELSEC 数据链接库范围 |
| | | m_strLastError = "Internal error detected by MELSEC Data Link Library."; |
| | | } |
| | | |
| | | if (nCode == 18944 || nCode == 18945) { |
| | | // 链接关联出错 |
| | | // 链接关联出错 |
| | | m_strLastError = "Link association error: Network does not exist, unsupported CPU, or incorrect network No./station number."; |
| | | } |
| | | |
| | | if (nCode >= 16384 && nCode <= 20479) { |
| | | // PLC CPU 检测范围 |
| | | // PLC CPU 检测范围 |
| | | m_strLastError = "Errors detected by the programmable controller CPU in the target station."; |
| | | } |
| | | |
| | | if (nCode >= 28416 && nCode <= 28671) { |
| | | // 冗余功能模块范围 |
| | | // 冗余功能模块范围 |
| | | m_strLastError = "Error detected in the redundancy module of the target station."; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检查连接状态和站点参数有效性 |
| | | // 检查连接状态和站点参数有效性 |
| | | int CPerformanceMelsec::ValidateStation(const StationIdentifier& station) const { |
| | | // 检查是否已连接 |
| | | // 检查是否已连接 |
| | | if (!m_bConnected.load()) { |
| | | return ERROR_CODE_NOT_CONNECTED; |
| | | } |
| | | |
| | | // 检查网络号和站点号范围 |
| | | // 检查网络号和站点号范围 |
| | | if (station.nNetNo < 0 || station.nNetNo > 239 || station.nStNo < 0 || station.nStNo > 255) { |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | return 0; // 参数有效 |
| | | return 0; // 参数有效 |
| | | } |
| | | |
| | | // 验证站点参数和数据有效性 |
| | | // 验证站点参数和数据有效性 |
| | | int CPerformanceMelsec::ValidateStationAndSize(const StationIdentifier& station, const short nCount) const { |
| | | // 验证站点参数 |
| | | // 验证站点参数 |
| | | const int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | return nRet; // 如果站点验证失败,返回对应错误码 |
| | | return nRet; // 如果站点验证失败,返回对应错误码 |
| | | } |
| | | |
| | | if (nCount <= 0) { |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | return 0; // 验证通过 |
| | | return 0; // 验证通过 |
| | | } |
| | | |
| | | // IP字符串转uint32_t |
| | | // IP字符串转uint32_t |
| | | bool CPerformanceMelsec::ConvertIpStringToUint32(const std::string& strIP, uint32_t& nIP) { |
| | | nIP = 0; |
| | | std::stringstream ss(strIP); |
| | |
| | | return true; |
| | | } |
| | | |
| | | //============================================静态辅助函数==================================================== |
| | | // 延时,并且转发窗口消息 |
| | | //============================================静态辅助函数==================================================== |
| | | // 延时,并且转发窗口消息 |
| | | void CPerformanceMelsec::Delay(const unsigned int nDelayMs) { |
| | | MSG message; |
| | | // 如果延迟时间为 0,仅处理一次消息队列 |
| | | // 如果延迟时间为 0,仅处理一次消息队列 |
| | | if (nDelayMs == 0) { |
| | | // 非阻塞的检查消息队列 |
| | | // 非阻塞的检查消息队列 |
| | | if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { |
| | | TranslateMessage(&message); // 将消息转化为有效的窗口消息 |
| | | DispatchMessage(&message); // 派发消息给相应的窗口过程 |
| | | TranslateMessage(&message); // 将消息转化为有效的窗口消息 |
| | | DispatchMessage(&message); // 派发消息给相应的窗口过程 |
| | | } |
| | | return; |
| | | } |
| | | |
| | | DWORD finish; |
| | | const DWORD start = GetTickCount(); // 获取当前的时间戳(从系统启动以来的毫秒数) |
| | | const DWORD start = GetTickCount(); // 获取当前的时间戳(从系统启动以来的毫秒数) |
| | | do { |
| | | if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { |
| | | TranslateMessage(&message); // 转换消息 |
| | | DispatchMessage(&message); // 处理消息 |
| | | TranslateMessage(&message); // 转换消息 |
| | | DispatchMessage(&message); // 处理消息 |
| | | } |
| | | Sleep(1); // 暂停 1 毫秒,防止过度占用 CPU |
| | | finish = GetTickCount(); // 获取当前的时间戳 |
| | | } while ((finish - start) < nDelayMs); // 循环直到经过的时间大于指定的延迟时间 |
| | | Sleep(1); // 暂停 1 毫秒,防止过度占用 CPU |
| | | finish = GetTickCount(); // 获取当前的时间戳 |
| | | } while ((finish - start) < nDelayMs); // 循环直到经过的时间大于指定的延迟时间 |
| | | } |
| | | |
| | | BoardType CPerformanceMelsec::FindBoardTypeByChannel(const int nChannel) { |
| | |
| | | return BoardType::UNKNOWN; |
| | | } |
| | | |
| | | // 合并网络号和站点号 |
| | | // 合并网络号和站点号 |
| | | short CPerformanceMelsec::CombineStation(const StationIdentifier& station) { |
| | | return static_cast<short>(station.nStNo | ((station.nNetNo << 8) & 0xFF00)); |
| | | } |
| | | |
| | | // 计算软元件类型 |
| | | // 计算软元件类型 |
| | | short CPerformanceMelsec::CalculateDeviceType(const StationIdentifier& station, DeviceType enDevType) { |
| | | int nDevType = static_cast<int>(enDevType); |
| | | |
| | | // 根据软元件类型的特定规则进行计算 |
| | | // 根据软元件类型的特定规则进行计算 |
| | | if (enDevType == DeviceType::LX || enDevType == DeviceType::LY || |
| | | enDevType == DeviceType::LB || enDevType == DeviceType::LW || |
| | | enDevType == DeviceType::LSB || enDevType == DeviceType::LSW) { |
| | | // 网络号加偏移 |
| | | // 网络号加偏移 |
| | | nDevType += station.nNetNo; |
| | | } |
| | | else if (enDevType == DeviceType::ER) { |
| | | // 文件寄存器的块号加偏移 |
| | | // 文件寄存器的块号加偏移 |
| | | nDevType += 0; |
| | | } |
| | | else if (enDevType == DeviceType::SPG) { |
| | | // 起始 I/O No. ÷ 16 的值 |
| | | // 起始 I/O No. ÷ 16 的值 |
| | | nDevType += 0 / 16; |
| | | } |
| | | |
| | | return static_cast<short>(nDevType); |
| | | } |
| | | |
| | | // std::vector<char>转换为std::vector<short> |
| | | // std::vector<char>转换为std::vector<short> |
| | | void CPerformanceMelsec::ConvertCharToShort(const std::vector<char>& vecChar, std::vector<short>& vecShort) { |
| | | vecShort.resize((vecChar.size() + 1) / 2, 0); // 调整 short 容器大小 |
| | | vecShort.resize((vecChar.size() + 1) / 2, 0); // 调整 short 容器大小 |
| | | for (size_t i = 0; i < vecChar.size(); i++) { |
| | | if (i % 2 == 0) { |
| | | vecShort[i / 2] = static_cast<unsigned char>(vecChar[i]); // 低字节 |
| | | vecShort[i / 2] = static_cast<unsigned char>(vecChar[i]); // 低字节 |
| | | } |
| | | else { |
| | | vecShort[i / 2] |= static_cast<unsigned char>(vecChar[i]) << 8; // 高字节 |
| | | vecShort[i / 2] |= static_cast<unsigned char>(vecChar[i]) << 8; // 高字节 |
| | | } |
| | | } |
| | | } |
| | | |
| | | // std::vector<short>转换为std::vector<char> |
| | | // std::vector<short>转换为std::vector<char> |
| | | void CPerformanceMelsec::ConvertShortToChar(const std::vector<short>& vecShort, std::vector<char>& vecChar) { |
| | | vecChar.resize(vecShort.size() * 2); // 调整 char 容器大小 |
| | | vecChar.resize(vecShort.size() * 2); // 调整 char 容器大小 |
| | | for (size_t i = 0; i < vecShort.size(); i++) { |
| | | vecChar[i * 2] = static_cast<char>(vecShort[i] & 0xFF); // 低字节 |
| | | vecChar[i * 2 + 1] = static_cast<char>((vecShort[i] >> 8) & 0xFF); // 高字节 |
| | | vecChar[i * 2] = static_cast<char>(vecShort[i] & 0xFF); // 低字节 |
| | | vecChar[i * 2 + 1] = static_cast<char>((vecShort[i] >> 8) & 0xFF); // 高字节 |
| | | } |
| | | } |
| | | |
| | | // std::vector<uint8_t>转换为std::vector<short> |
| | | // std::vector<uint8_t>转换为std::vector<short> |
| | | void CPerformanceMelsec::ConvertUint8ToShort(const std::vector<uint8_t>& vecUint8, std::vector<short>& vecShort) { |
| | | vecShort.resize((vecUint8.size() + 1) / 2, 0); // 调整 short 容器大小 |
| | | vecShort.resize((vecUint8.size() + 1) / 2, 0); // 调整 short 容器大小 |
| | | for (size_t i = 0; i < vecUint8.size(); i++) { |
| | | if (i % 2 == 0) { |
| | | vecShort[i / 2] = static_cast<short>(vecUint8[i]); // 低字节 |
| | | vecShort[i / 2] = static_cast<short>(vecUint8[i]); // 低字节 |
| | | } |
| | | else { |
| | | vecShort[i / 2] |= static_cast<short>(vecUint8[i] << 8); // 高字节 |
| | | vecShort[i / 2] |= static_cast<short>(vecUint8[i] << 8); // 高字节 |
| | | } |
| | | } |
| | | } |
| | | |
| | | // std::vector<short>转换为std::vector<uint8_t> |
| | | // std::vector<short>转换为std::vector<uint8_t> |
| | | void CPerformanceMelsec::ConvertShortToUint8(const std::vector<short>& vecShort, std::vector<uint8_t>& vecUint8) { |
| | | vecUint8.resize(vecShort.size() * 2); // 调整 uint8_t 容器大小 |
| | | vecUint8.resize(vecShort.size() * 2); // 调整 uint8_t 容器大小 |
| | | for (size_t i = 0; i < vecShort.size(); i++) { |
| | | vecUint8[i * 2] = static_cast<uint8_t>(vecShort[i] & 0xFF); // 低字节 |
| | | vecUint8[i * 2 + 1] = static_cast<uint8_t>((vecShort[i] >> 8) & 0xFF); // 高字节 |
| | | vecUint8[i * 2] = static_cast<uint8_t>(vecShort[i] & 0xFF); // 低字节 |
| | | vecUint8[i * 2 + 1] = static_cast<uint8_t>((vecShort[i] >> 8) & 0xFF); // 高字节 |
| | | } |
| | | } |
| | | |
| | | // std::vector<uint32_t>转换为std::vector<short> |
| | | // std::vector<uint32_t>转换为std::vector<short> |
| | | void CPerformanceMelsec::ConvertUint32ToShort(const std::vector<uint32_t>& vecUint32, std::vector<short>& vecShort) { |
| | | vecShort.resize(vecUint32.size() * 2); // 每个 uint32_t 转换为两个 short |
| | | vecShort.resize(vecUint32.size() * 2); // 每个 uint32_t 转换为两个 short |
| | | for (size_t i = 0; i < vecUint32.size(); i++) { |
| | | vecShort[i * 2] = static_cast<short>(vecUint32[i] & 0xFFFF); // 低16位 |
| | | vecShort[i * 2 + 1] = static_cast<short>((vecUint32[i] >> 16) & 0xFFFF); // 高16位 |
| | | vecShort[i * 2] = static_cast<short>(vecUint32[i] & 0xFFFF); // 低16位 |
| | | vecShort[i * 2 + 1] = static_cast<short>((vecUint32[i] >> 16) & 0xFFFF); // 高16位 |
| | | } |
| | | } |
| | | |
| | | // std::vector<short>转换为std::vector<uint32_t> |
| | | // std::vector<short>转换为std::vector<uint32_t> |
| | | void CPerformanceMelsec::ConvertShortToUint32(const std::vector<short>& vecShort, std::vector<uint32_t>& vecUint32) { |
| | | vecUint32.resize((vecShort.size() + 1) / 2, 0); // 每两个 short 合并为一个 uint32_t |
| | | vecUint32.resize((vecShort.size() + 1) / 2, 0); // 每两个 short 合并为一个 uint32_t |
| | | for (size_t i = 0; i < vecUint32.size(); i++) { |
| | | vecUint32[i] = (static_cast<uint32_t>(static_cast<uint16_t>(vecShort[i * 2 + 1])) << 16) | // 高16位 |
| | | static_cast<uint32_t>(static_cast<uint16_t>(vecShort[i * 2])); // 低16位 |
| | | vecUint32[i] = (static_cast<uint32_t>(static_cast<uint16_t>(vecShort[i * 2 + 1])) << 16) | // 高16位 |
| | | static_cast<uint32_t>(static_cast<uint16_t>(vecShort[i * 2])); // 低16位 |
| | | } |
| | | } |
| | | |
| | | //============================================模板辅助函数==================================================== |
| | | // 验证站点参数和数据有效性 |
| | | //============================================模板辅助函数==================================================== |
| | | // 验证站点参数和数据有效性 |
| | | template <typename T> |
| | | int CPerformanceMelsec::ValidateStationAndData(const StationIdentifier& station, const std::vector<T>& vecData) { |
| | | // 验证站点参数 |
| | | // 验证站点参数 |
| | | const int nRet = ValidateStation(station); |
| | | if (nRet != 0) { |
| | | return nRet; // 如果站点验证失败,返回对应错误码 |
| | | return nRet; // 如果站点验证失败,返回对应错误码 |
| | | } |
| | | |
| | | // 验证数据是否为空 |
| | | // 验证数据是否为空 |
| | | if (vecData.empty()) { |
| | | return ERROR_CODE_INVALID_PARAM; |
| | | } |
| | | |
| | | return 0; // 验证通过 |
| | | return 0; // 验证通过 |
| | | } |
| | | |
| | | // 由低转高容器的模板(整型) |
| | | // 由低转高容器的模板(整型) |
| | | template <typename T, typename U> |
| | | void CPerformanceMelsec::ConvertLowToHigh(const std::vector<T>& vecLow, std::vector<U>& vecHigh) { |
| | | static_assert(std::is_integral<T>::value && std::is_integral<U>::value, "T and U must be integral types"); |
| | | |
| | | // 自动计算 nGroupSize |
| | | // 自动计算 nGroupSize |
| | | constexpr size_t nGroupSize = sizeof(U) / sizeof(T); |
| | | |
| | | // 如果 T 和 U 的大小相等,直接转换 |
| | | // 如果 T 和 U 的大小相等,直接转换 |
| | | if (sizeof(T) == sizeof(U)) { |
| | | vecHigh.assign(vecLow.begin(), vecLow.end()); |
| | | return; |
| | | } |
| | | |
| | | // 如果 U 的大小是 T 的倍数,正常组合 |
| | | // 如果 U 的大小是 T 的倍数,正常组合 |
| | | static_assert(sizeof(U) > sizeof(T), "Size of U must be greater than or equal to size of T"); |
| | | |
| | | // 计算完整组的数量 |
| | | size_t nHighSize = (vecLow.size() + nGroupSize - 1) / nGroupSize; // 向上取整 |
| | | // 计算完整组的数量 |
| | | size_t nHighSize = (vecLow.size() + nGroupSize - 1) / nGroupSize; // 向上取整 |
| | | vecHigh.resize(nHighSize, 0); |
| | | |
| | | // 合并低位数据到高位数据 |
| | | // 合并低位数据到高位数据 |
| | | for (size_t i = 0; i < vecLow.size(); i++) { |
| | | vecHigh[i / nGroupSize] |= (static_cast<U>(vecLow[i]) << ((i % nGroupSize) * CHAR_BIT * sizeof(T))); |
| | | } |
| | |
| | | return vecHigh; |
| | | } |
| | | |
| | | // 由高转低容器的模板(整型) |
| | | // 由高转低容器的模板(整型) |
| | | template <typename T, typename U> |
| | | void CPerformanceMelsec::ConvertHighToLow(const std::vector<T>& vecHigh, std::vector<U>& vecLow) { |
| | | static_assert(std::is_integral<T>::value && std::is_integral<U>::value, "T and U must be integral types"); |
| | | |
| | | // 自动计算 nGroupSize |
| | | // 自动计算 nGroupSize |
| | | constexpr size_t nGroupSize = sizeof(T) / sizeof(U); |
| | | |
| | | // 如果 T 和 U 的大小相等,直接转换 |
| | | // 如果 T 和 U 的大小相等,直接转换 |
| | | if (sizeof(T) == sizeof(U)) { |
| | | vecLow.assign(vecHigh.begin(), vecHigh.end()); |
| | | return; |
| | | } |
| | | |
| | | // 如果 T 的大小是 U 的倍数,正常分解 |
| | | // 如果 T 的大小是 U 的倍数,正常分解 |
| | | static_assert(sizeof(T) > sizeof(U), "Size of T must be greater than or equal to size of U"); |
| | | |
| | | size_t nLowSize = vecHigh.size() * nGroupSize; // 低容器的大小 |
| | | size_t nLowSize = vecHigh.size() * nGroupSize; // 低容器的大小 |
| | | vecLow.resize(nLowSize, 0); |
| | | |
| | | // 分解高位数据到低位数据 |
| | | // 分解高位数据到低位数据 |
| | | for (size_t i = 0; i < vecHigh.size(); i++) { |
| | | for (size_t j = 0; j < nGroupSize; j++) { |
| | | vecLow[i * nGroupSize + j] = static_cast<U>((vecHigh[i] >> (j * CHAR_BIT * sizeof(U))) & ((1ULL << (CHAR_BIT * sizeof(U))) - 1)); |