SourceCode/Bond/SGMeasurement/CCLinkPerformance/CCLinkIEControl.cpp
@@ -40,7 +40,7 @@ return ERROR_CODE_INVALID_DATA; } // 解析各位状态 // 解析各位状态 const short nBuffer = vecLedBuffer[0]; outLedStatus.bExtPw = (nBuffer & (1 << 15)) != 0; outLedStatus.bRd = (nBuffer & (1 << 6)) != 0; @@ -56,40 +56,40 @@ CCLinkIEControlMode CCCLinkIEControl::ConvertToCCLinkIEControlMode(const short nMode) { switch (static_cast<CCLinkIEControlMode>(nMode)) { case CCLinkIEControlMode::ONLINE: return CCLinkIEControlMode::ONLINE; // 在线 case CCLinkIEControlMode::OFFLINE: return CCLinkIEControlMode::OFFLINE; // 离线 case CCLinkIEControlMode::INTER_STATION_TEST: return CCLinkIEControlMode::INTER_STATION_TEST; // 站间测试 case CCLinkIEControlMode::LINE_TEST: return CCLinkIEControlMode::LINE_TEST; // 线路测试 case CCLinkIEControlMode::LOOPBACK_TEST: return CCLinkIEControlMode::LOOPBACK_TEST; // 自回送测试 case CCLinkIEControlMode::HW_TEST: return CCLinkIEControlMode::HW_TEST; // H/W测试 case CCLinkIEControlMode::BUS_IF_TEST: return CCLinkIEControlMode::BUS_IF_TEST; // 总线I/F测试 case CCLinkIEControlMode::ONLINE: return CCLinkIEControlMode::ONLINE; // 在线 case CCLinkIEControlMode::OFFLINE: return CCLinkIEControlMode::OFFLINE; // 离线 case CCLinkIEControlMode::INTER_STATION_TEST: return CCLinkIEControlMode::INTER_STATION_TEST; // 站间测试 case CCLinkIEControlMode::LINE_TEST: return CCLinkIEControlMode::LINE_TEST; // 线路测试 case CCLinkIEControlMode::LOOPBACK_TEST: return CCLinkIEControlMode::LOOPBACK_TEST; // 自回送测试 case CCLinkIEControlMode::HW_TEST: return CCLinkIEControlMode::HW_TEST; // H/W测试 case CCLinkIEControlMode::BUS_IF_TEST: return CCLinkIEControlMode::BUS_IF_TEST; // 总线I/F测试 default: return CCLinkIEControlMode::UNKNOWN; } } int CCCLinkIEControl::ValidateBoardStatus(const BoardStatus& status) { if (status.nStationValue < 1 || status.nStationValue > 120) { return ERROR_CODE_STATION_OUT_OF_RANGE; // 站号超出范围 return ERROR_CODE_STATION_OUT_OF_RANGE; // 站号超出范围 } if (status.nGroupValue < 0 || status.nGroupValue > 32) { return ERROR_CODE_GROUP_OUT_OF_RANGE; // 组超出范围 return ERROR_CODE_GROUP_OUT_OF_RANGE; // 组超出范围 } if (status.nNetworkValue < 1 || status.nNetworkValue > 239) { return ERROR_CODE_NETWORK_OUT_OF_RANGE; // 网络号超出范围 return ERROR_CODE_NETWORK_OUT_OF_RANGE; // 网络号超出范围 } return 0; // 校验通过 return 0; // 校验通过 } int CCCLinkIEControl::ReadDataEx(const StationIdentifier& station, DeviceType enDevType, long devNo, long size, void* pData) { // 验证站点参数和数据有效性 // 验证站点参数和数据有效性 int nRet = ValidateStationAndSize(station, static_cast<short>(size)); if (nRet != 0) { UpdateLastError(nRet); return nRet; } // 确保线程安全的最小锁定范围 // 确保线程安全的最小锁定范围 { std::lock_guard<std::mutex> lock(m_mtx); const short nDevType = CalculateDeviceType(station, enDevType); SourceCode/Bond/SGMeasurement/CCLinkPerformance/CCLinkIEControl.h
@@ -4,14 +4,14 @@ #include "PerformanceMelsec.h" enum class CCLinkIEControlMode : short { UNKNOWN = 0x0194, // δ֪ ONLINE = 0x0000, // 在线 OFFLINE = 0x0002, // 离线 INTER_STATION_TEST = 0x0005, // 站间测试 LINE_TEST = 0x0006, // 线路测试 LOOPBACK_TEST = 0x0007, // 自回送测试 HW_TEST = 0x0009, // H/W测试 BUS_IF_TEST = 0x000E // 总线I/F测试 UNKNOWN = 0x0194, // 未知 ONLINE = 0x0000, // 在线 OFFLINE = 0x0002, // 离线 INTER_STATION_TEST = 0x0005, // 站间测试 LINE_TEST = 0x0006, // 线路测试 LOOPBACK_TEST = 0x0007, // 自回送测试 HW_TEST = 0x0009, // H/W测试 BUS_IF_TEST = 0x000E // 总线I/F测试 }; class CCCLinkIEControl final : public CPerformanceMelsec { @@ -20,16 +20,16 @@ ~CCCLinkIEControl() override; struct LedStatus { bool bExtPw; // 外部电源状态 (b15) bool bRd; // 数据接收状态 (b6) bool bDLnk; // 数据链接状态 (b5) bool bPrm; // 管理功能状态 (b4) bool bErr; // 错误状态 (b3) bool bSd; // 数据发送状态 (b2) bool bMode; // 动作模式 (b1) bool bRun; // 运行状态 (b0) bool bExtPw; // 外部电源状态 (b15) bool bRd; // 数据接收状态 (b6) bool bDLnk; // 数据链接状态 (b5) bool bPrm; // 管理功能状态 (b4) bool bErr; // 错误状态 (b3) bool bSd; // 数据发送状态 (b2) bool bMode; // 动作模式 (b1) bool bRun; // 运行状态 (b0) // 转换为字符串,用于调试 // 转换为字符串,用于调试 std::string ToString() const { std::ostringstream oss; oss << "CC-Link IE Control Network LED Status: {" @@ -46,17 +46,17 @@ } }; // 读取目标站点CPU类型 // 读取目标站点CPU类型 // short ReadCPUCodeEx(const StationIdentifier& station, short& nCPUCode); // 板模式获取/设置 // 板模式获取/设置 int SetBoardModeEx(CCLinkIEControlMode mode); CCLinkIEControlMode GetBoardModeEx(); // 获取板状态 // 获取板状态 int GetBoardStatusEx(BoardStatus& status); // 读取LED状态 // 读取LED状态 int ReadLedStatus(LedStatus& outLedStatus); int ReadDataEx(const StationIdentifier& station, DeviceType enDevType, long devNo, long size, void* pData); SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.cpp
@@ -23,9 +23,9 @@ #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."}, @@ -118,7 +118,7 @@ {-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."}, @@ -137,26 +137,26 @@ 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; @@ -167,7 +167,7 @@ return true; } // 加载错误信息 // 加载错误信息 bool CPerformanceMelsec::LoadErrorInfoFromFile(const std::string& filename) { std::ifstream inFile(filename); if (!inFile.is_open()) { @@ -183,7 +183,7 @@ std::string strToken; std::string strMessage; // 使用分隔符 "|" 解析每一行 // 使用分隔符 "|" 解析每一行 if (std::getline(iss, strToken, '|')) { nCode = std::stoi(strToken); } @@ -200,7 +200,7 @@ return true; } // 连接到PLC // 连接到PLC int CPerformanceMelsec::Connect(const short nChannel, const short nMode) { std::lock_guard<std::mutex> lock(m_mtx); @@ -214,7 +214,7 @@ return ERROR_CODE_INVALID_PARAM; } // 连接PLC,显式类型转换以匹配 mdOpen 的签名 // 连接PLC,显式类型转换以匹配 mdOpen 的签名 const short nRet = mdOpen(nChannel, nMode, &m_nPath); if (nRet == 0) { m_bConnected.store(true); @@ -228,7 +228,7 @@ return nRet; } // 断开连接 // 断开连接 int CPerformanceMelsec::Disconnect() { std::lock_guard<std::mutex> lock(m_mtx); @@ -245,7 +245,7 @@ return nRet; } // 可编程控制器软元件信息表的初始化 // 可编程控制器软元件信息表的初始化 int CPerformanceMelsec::InitializeController() { std::lock_guard<std::mutex> lock(m_mtx); @@ -263,14 +263,14 @@ 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) { @@ -279,7 +279,7 @@ return nRet; } // 填充版本信息到结构体 // 填充版本信息到结构体 version.fixedValue[0] = static_cast<char>(buf[0] & 0xFF); version.fixedValue[1] = static_cast<char>((buf[0] >> 8) & 0xFF); @@ -310,16 +310,16 @@ 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); @@ -334,15 +334,15 @@ 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); @@ -357,9 +357,9 @@ return nRet; } // 获取板模式 // 获取板模式 int CPerformanceMelsec::GetBoardMode(short& nMode) { // 检查是否已经连接 // 检查是否已经连接 if (!m_bConnected.load()) { UpdateLastError(ERROR_CODE_NOT_CONNECTED); return ERROR_CODE_NOT_CONNECTED; @@ -381,7 +381,7 @@ return 0; } // 板复位 // 板复位 int CPerformanceMelsec::BoardReset() { std::lock_guard<std::mutex> lock(m_mtx); if (!m_bConnected.load()) { @@ -398,7 +398,7 @@ return nRet; } // 板LED读取 // 板LED读取 int CPerformanceMelsec::ReadBoardLed(std::vector<short>& vecLedBuffer) { std::lock_guard<std::mutex> lock(m_mtx); if (!m_bConnected.load()) { @@ -406,11 +406,11 @@ 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); @@ -421,7 +421,7 @@ return nRet; } // 获取板状态 // 获取板状态 int CPerformanceMelsec::GetBoardStatus(BoardStatus& status) { std::lock_guard<std::mutex> lock(m_mtx); if (!m_bConnected) { @@ -436,25 +436,25 @@ LOG_ERROR(m_strLastError); } // 将 buf 映射到结构体 // 将 buf 映射到结构体 status = BoardStatus::fromBuffer(buf); return 0; } // 通用读数据 // 通用读数据 int CPerformanceMelsec::ReadData(const StationIdentifier& station, const long nDevType, const long nDevNo, long nSize, std::vector<short>& vecData) { // 验证站点参数和数据有效性 // 验证站点参数和数据有效性 int nRet = ValidateStationAndSize(station, static_cast<short>(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(); @@ -468,15 +468,15 @@ } 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); @@ -490,7 +490,7 @@ } const short nDevType = CalculateDeviceType(station, enDevType); const auto nSize = static_cast<short>((static_cast<int>(nBitCount) + 15) / 16); // 计算需要读取的字数量(向上取整) 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); @@ -498,15 +498,15 @@ if (nRet == 0) { vecData.clear(); // 将字数据解析为位数据 // 将字数据解析为位数据 for (short nIdx = 0; nIdx < nSize; ++nIdx) { const short nCurrentValue = vecTempBuffer[nIdx]; // 遍历当前 short 中的每一位 // 遍历当前 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; // 如果已经读取完所需的位数,提前退出 } } } @@ -515,9 +515,9 @@ 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); @@ -536,44 +536,44 @@ 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 long nDevType, const long nDevNo, long 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); @@ -588,9 +588,9 @@ return nRet; } // 写位数据 // 写位数据 int CPerformanceMelsec::WriteBitData(const StationIdentifier& station, const DeviceType enDevType, const short nDevNo, const BitContainer& vecData) { // 验证站点参数和数据有效性 // 验证站点参数和数据有效性 int nRet = ValidateStationAndData(station, vecData); if (nRet != 0) { UpdateLastError(nRet); @@ -604,16 +604,16 @@ } const short nDevType = CalculateDeviceType(station, enDevType); const auto nSize = static_cast<short>((static_cast<int>(vecData.size()) + 15) / 16); // 计算需要写入的字数量(向上取整) const auto nSize = static_cast<short>((static_cast<int>(vecData.size()) + 15) / 16); // 计算需要写入的字数量(向上取整) // 准备临时缓冲区来存储转换后的 16 位数据 // 准备临时缓冲区来存储转换后的 16 位数据 std::vector<short> vecTempBuffer(nSize, 0); { std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 // 将位数据按字打包到临时缓冲区 std::lock_guard<std::mutex> lock(m_mtx); // 线程安全保护 // 将位数据按字打包到临时缓冲区 for (int i = 0; i < vecData.size(); ++i) { if (vecData[i]) { // 使用 & 0xFFFF 保证不会超过 16 位,防止溢出 // 使用 & 0xFFFF 保证不会超过 16 位,防止溢出 vecTempBuffer[i / 16] |= static_cast<short>((1 << (i % 16)) & 0xFFFF); } } @@ -622,16 +622,16 @@ 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())); @@ -639,30 +639,30 @@ 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); @@ -678,7 +678,7 @@ 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()); } @@ -687,7 +687,7 @@ 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); } @@ -695,7 +695,7 @@ return 0; } // 扩展读取位数据 // 扩展读取位数据 long CPerformanceMelsec::ReadBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nBitCount, BitContainer& vecData) { long nRet = ValidateStationAndSize(station, static_cast<short>(nBitCount)); if (nRet != 0) { @@ -705,7 +705,7 @@ const short nDevType = CalculateDeviceType(station, enDevType); // === 自动对齐到起始字 === // === 自动对齐到起始字 === long nWordAlignedStartBit = nDevNo / 16 * 16; long nBitOffset = nDevNo - nWordAlignedStartBit; long nTotalBits = nBitOffset + nBitCount; @@ -735,7 +735,7 @@ return 0; } // 扩展读取字数据 // 扩展读取字数据 long CPerformanceMelsec::ReadWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nWordCount, WordContainer& vecData) { long nRet = ValidateStationAndSize(station, static_cast<short>(nWordCount)); if (nRet != 0) { @@ -762,7 +762,7 @@ return 0; } // 扩展读取双字数据 // 扩展读取双字数据 long CPerformanceMelsec::ReadDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nDWordCount, DWordContainer& vecData) { long nRet = ValidateStationAndSize(station, static_cast<short>(nDWordCount)); if (nRet != 0) { @@ -791,27 +791,27 @@ 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); @@ -820,7 +820,7 @@ return nRet; } // 扩展写位数据 // 扩展写位数据 long CPerformanceMelsec::WriteBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const BitContainer& vecData) { long nRet = ValidateStationAndData(station, vecData); if (nRet != 0) { @@ -830,13 +830,13 @@ const short nDevType = CalculateDeviceType(station, enDevType); // === 1. 自动对齐起始地址 === // === 1. 自动对齐起始地址 === long nWordAlignedStartBit = nDevNo / 16 * 16; long nBitOffset = nDevNo - nWordAlignedStartBit; long nTotalBits = nBitOffset + static_cast<long>(vecData.size()); size_t nWordCount = (nTotalBits + 15) / 16; // === 2. 先读取原始值以支持非对齐覆盖 === // === 2. 先读取原始值以支持非对齐覆盖 === std::vector<char> vecRaw; nRet = ReadDataEx(station, nDevType, nWordAlignedStartBit, static_cast<long>(nWordCount * sizeof(short)), vecRaw); if (nRet != 0) { @@ -844,7 +844,7 @@ return nRet; } // === 3. 合并新数据 === // === 3. 合并新数据 === std::vector<short> vecWordBuffer(nWordCount, 0); for (size_t i = 0; i < nWordCount; ++i) { vecWordBuffer[i] = static_cast<unsigned char>(vecRaw[i * 2]) | (static_cast<unsigned char>(vecRaw[i * 2 + 1]) << 8); @@ -862,7 +862,7 @@ } } // === 4. 转为字节流写入 === // === 4. 转为字节流写入 === std::vector<char> vecByteBuffer(nWordCount * 2); for (size_t i = 0; i < nWordCount; ++i) { vecByteBuffer[i * 2] = static_cast<char>(vecWordBuffer[i] & 0xFF); @@ -872,7 +872,7 @@ return WriteDataEx(station, nDevType, nWordAlignedStartBit, vecByteBuffer); } // 扩展写字数据 // 扩展写字数据 long CPerformanceMelsec::WriteWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const WordContainer& vecData) { long nRet = ValidateStationAndData(station, vecData); if (nRet != 0) { @@ -892,7 +892,7 @@ return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer); } // 扩展写双字数据 // 扩展写双字数据 long CPerformanceMelsec::WriteDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const DWordContainer& vecData) { long nRet = ValidateStationAndData(station, vecData); if (nRet != 0) { @@ -914,7 +914,7 @@ return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer); } // 扩展软元件随机读取 // 扩展软元件随机读取 long CPerformanceMelsec::ReadRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, std::vector<char>& vecData) { if (vecSoftElements.empty()) { UpdateLastError(ERROR_INVALID_PARAMETER); @@ -922,48 +922,48 @@ 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); @@ -971,26 +971,26 @@ 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); @@ -999,9 +999,9 @@ 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); @@ -1016,37 +1016,37 @@ 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()); } @@ -1059,7 +1059,7 @@ 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)) { @@ -1067,14 +1067,14 @@ 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()); } @@ -1083,14 +1083,14 @@ 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)) { @@ -1098,13 +1098,13 @@ 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()); } @@ -1117,18 +1117,18 @@ 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); } @@ -1141,16 +1141,16 @@ 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); @@ -1165,11 +1165,11 @@ return nRet; } // 扩展位软元件设置 // 扩展位软元件设置 long CPerformanceMelsec::SetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo) { std::lock_guard<std::mutex> lock(m_mtx); // 检查参数有效性 // 检查参数有效性 long nRet = ValidateStation(station); if (nRet != 0) { UpdateLastError(nRet); @@ -1186,11 +1186,11 @@ return nRet; } // 扩展位软元件复位 // 扩展位软元件复位 long CPerformanceMelsec::ResetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo) { std::lock_guard<std::mutex> lock(m_mtx); // 检查参数有效性 // 检查参数有效性 long nRet = ValidateStation(station); if (nRet != 0) { UpdateLastError(nRet); @@ -1207,23 +1207,23 @@ 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); @@ -1237,7 +1237,7 @@ return nRet; } // 事件等待 // 事件等待 int CPerformanceMelsec::WaitForBoardEvent(std::vector<short> vecEventNumbers, const int nTimeoutMs, EventDetails& details) { std::lock_guard<std::mutex> lock(m_mtx); @@ -1251,12 +1251,12 @@ 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); @@ -1269,100 +1269,100 @@ 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); @@ -1381,30 +1381,30 @@ 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) { @@ -1426,136 +1426,136 @@ 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))); } @@ -1563,27 +1563,27 @@ 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)); SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.h
@@ -12,151 +12,151 @@ #include <sstream> #include <unordered_map> // 连接参数 #define PLC_MAX_RETRY 3 // 最大重试次数:在与PLC通信时,如果发生通信错误,将最多重试3次 #define PLC_TIMEOUT 500 // 超时时间(毫秒):每次通信操作的超时等待时间为500毫秒 // 连接参数 #define PLC_MAX_RETRY 3 // 最大重试次数:在与PLC通信时,如果发生通信错误,将最多重试3次 #define PLC_TIMEOUT 500 // 超时时间(毫秒):每次通信操作的超时等待时间为500毫秒 /* * 网络通道:指定通信所使用的网络通道号,通常在多通道通信中设置 * 51 到 54 是 MELSECNET/H 的 1-4 通道 * 81 到 84 是 CC-Link 的 1-4 通道 * 151 到 154 是 CC-Link IE 控制器网络的 1-4 通道 * 181 到 184 是 CC-Link IE 现场网络的 1-4 通道 * 281 到 284 是 CC-Link IE TSN 网络的 1-4 通道 * 网络通道:指定通信所使用的网络通道号,通常在多通道通信中设置 * 51 到 54 是 MELSECNET/H 的 1-4 通道 * 81 到 84 是 CC-Link 的 1-4 通道 * 151 到 154 是 CC-Link IE 控制器网络的 1-4 通道 * 181 到 184 是 CC-Link IE 现场网络的 1-4 通道 * 281 到 284 是 CC-Link IE TSN 网络的 1-4 通道 **/ #define MELSECNET_CHANNEL(x) (50 + (x)) // x 范围:1~4 #define CC_LINK_CHANNEL(x) (80 + (x)) // x 范围:1~4 #define CC_LINK_IE_CONTROL_CHANNEL(x) (150 + (x)) // x 范围:1~4 #define CC_LINK_IE_FIELD_CHANNEL(x) (180 + (x)) // x 范围:1~4 #define CC_LINK_IE_TSN_CHANNEL(x) (280 + (x)) // x 范围:1~4 #define MELSECNET_CHANNEL(x) (50 + (x)) // x 范围:1~4 #define CC_LINK_CHANNEL(x) (80 + (x)) // x 范围:1~4 #define CC_LINK_IE_CONTROL_CHANNEL(x) (150 + (x)) // x 范围:1~4 #define CC_LINK_IE_FIELD_CHANNEL(x) (180 + (x)) // x 范围:1~4 #define CC_LINK_IE_TSN_CHANNEL(x) (280 + (x)) // x 范围:1~4 // 自定义错误码 #define ERROR_CODE_UNKNOWN 0x00010000 // δ֪ #define ERROR_CODE_NOT_CONNECTED 0x00020000 // 未连接 #define ERROR_CODE_INVALID_PARAM 0x00030000 // 参数无效 #define ERROR_CODE_INVALID_DATA 0x00040000 // 数据无效 #define ERROR_CODE_STATION_OUT_OF_RANGE 0x00050000 // 站号超出范围 #define ERROR_CODE_GROUP_OUT_OF_RANGE 0x00060000 // 组号超出范围 #define ERROR_CODE_NETWORK_OUT_OF_RANGE 0x00070000 // 网络号超出范围 // 自定义错误码 #define ERROR_CODE_UNKNOWN 0x00010000 // 未知 #define ERROR_CODE_NOT_CONNECTED 0x00020000 // 未连接 #define ERROR_CODE_INVALID_PARAM 0x00030000 // 参数无效 #define ERROR_CODE_INVALID_DATA 0x00040000 // 数据无效 #define ERROR_CODE_STATION_OUT_OF_RANGE 0x00050000 // 站号超出范围 #define ERROR_CODE_GROUP_OUT_OF_RANGE 0x00060000 // 组号超出范围 #define ERROR_CODE_NETWORK_OUT_OF_RANGE 0x00070000 // 网络号超出范围 // 板块类型 // 板块类型 enum class BoardType { UNKNOWN = -1, // 未知类型 UNKNOWN = -1, // 未知类型 MELSECNET_H = MELSECNET_CHANNEL(1), // MELSECNET/H CC_LINK_VER_2 = CC_LINK_CHANNEL(1), // CC-Link Ver. 2 CC_LINK_IE_CONTROL = CC_LINK_IE_CONTROL_CHANNEL(1), // CC-Link IE 控制网络 CC_LINK_IE_FIELD = CC_LINK_IE_FIELD_CHANNEL(1), // CC-Link IE 现场网络 CC_LINK_IE_CONTROL = CC_LINK_IE_CONTROL_CHANNEL(1), // CC-Link IE 控制网络 CC_LINK_IE_FIELD = CC_LINK_IE_FIELD_CHANNEL(1), // CC-Link IE 现场网络 CC_LINK_IE_TSN = CC_LINK_IE_TSN_CHANNEL(1) // CC-Link IE TSN }; // 软元件类型枚举 // 软元件类型枚举 enum class DeviceType { /* * ER、LX、LY、LB、LW、LSB、LSW和SPG软元件都是范围型 * ER:DevER0~256 * LX:DevLX1~255,DevLX(x) (DevX*1000+(x)) * LY:DevLY1~255,DevLY(x) (DevY*1000+(x)) * LB:DevLB1~255,DevLB(x) (DevB*1000+(x)) * LW:DevLW1~255,DevLW(x) (DevW*1000+(x)) * LSB:DevLSB1~255,DevLSB(x) (DevQSB*1000+(x)) * LSW:DevLSW1~255,DevLSW(x) (DevQSW*1000+(x)) * SPG:DevSPG0~255,DevSPG(x) (29*1000+(x)) * 扩展文件寄存器代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定块No.(0~256) * 链接直接软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定网络No.(1~255) * 智能功能模块软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定(起始I/ONo.÷16)的值 * 扩展文件寄存器和链接直接软元件在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束 * MAIL和MAILMC在SEND功能及RECV功能中,与软元件访问一样,指定各功能对应的软元件类型,进行数据的发送(mdSend、mdSendEx)或数据的读取(mdReceive、mdReceiveEx) * ER、LX、LY、LB、LW、LSB、LSW和SPG软元件都是范围型 * ER:DevER0~256 * LX:DevLX1~255,DevLX(x) (DevX*1000+(x)) * LY:DevLY1~255,DevLY(x) (DevY*1000+(x)) * LB:DevLB1~255,DevLB(x) (DevB*1000+(x)) * LW:DevLW1~255,DevLW(x) (DevW*1000+(x)) * LSB:DevLSB1~255,DevLSB(x) (DevQSB*1000+(x)) * LSW:DevLSW1~255,DevLSW(x) (DevQSW*1000+(x)) * SPG:DevSPG0~255,DevSPG(x) (29*1000+(x)) * 扩展文件寄存器代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定块No.(0~256) * 链接直接软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定网络No.(1~255) * 智能功能模块软元件代码指定(10进制数)的后3位数及软元件名指定的数值中,应指定(起始I/ONo.÷16)的值 * 扩展文件寄存器和链接直接软元件在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束 * MAIL和MAILMC在SEND功能及RECV功能中,与软元件访问一样,指定各功能对应的软元件类型,进行数据的发送(mdSend、mdSendEx)或数据的读取(mdReceive、mdReceiveEx) **/ X = 0x0001, // 输入 (位) Y = 0x0002, // 输出 (位) L = 0x0003, // 锁存继电器 (位) M = 0x0004, // 内部继电器 (位) SM = 0x0005, // 特殊继电器 (位) F = 0x0006, // 报警器 (位) TT = 0x0007, // 定时器 (触点) (位) TC = 0x0008, // 计数器 (线圈) (位) CT = 0x0009, // 计数器 (触点) (位) CC = 0x000A, // 计数器 (线圈) (字) TN = 0x000B, // 定时器 (当前值) (字) CN = 0x000C, // 计数器 (当前值) (字) D = 0x000D, // 数据寄存器 (字) SD = 0x000E, // 特殊寄存器 (字) TM = 0x000F, // 定时器 (设置值主) (字) TS = 0x0010, // 定时器 (设置值主1) (字) TS2 = 0x3E82, // 定时器 (设置值主2) (字) TS3 = 0x3E83, // 定时器 (设置值主3) (字) CM = 0x0011, // 计数器 (设置值主) (字) CS = 0x0012, // 计数器 (设置值主1) (字) CS2 = 0x4652, // 计数器 (设置值主2) (字) CS3 = 0x4653, // 计数器 (设置值主3) (字) A = 0x0013, // 累加器 (字) Z = 0x0014, // 变址寄存器 (字) V = 0x0015, // 变址寄存器 (字) R = 0x0016, // 文件寄存器 (块切换方式) (字) ER = 0x55F0, // 扩展文件寄存器 (块切换方式) (0x55F0~0x56F0) (字) (在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束。(读取数据不正确。)) ZR = 0x00DC, // 文件寄存器 (连号访问方式) (字) B = 0x0017, // 链接继电器 (位) W = 0x0018, // 链接寄存器 (字) QSB = 0x0019, // 链接特殊继电器 (位) STT = 0x001A, // 累计定时器 (触点) (位) STC = 0x001B, // 累计定时器 (线圈) (位) QSW = 0x001C, // 链接特殊寄存器 (字) QV = 0x001E, // 变址继电器 (位) MRB = 0x0021, // 随机访问缓冲 (字) STN = 0x0023, // 累计定时器 (当前值) (字) LZ = 0x0026, // 超长变址寄存器 (双字) RD = 0x0027, // 刷新数据寄存器 (字) LTT = 0x0029, // 超长定时器 (触点) (位) LTC = 0x002A, // 超长定时器 (线圈) (位) LTN = 0x002B, // 超长定时器 (当前值) (双字) LCT = 0x002C, // 超长计数器 (触点) (位) LCC = 0x002D, // 超长计数器 (线圈) (位) LCN = 0x002E, // 超长计数器 (当前值) (双字) LSTT = 0x002F, // 超长累计定时器 (触点) (位) LSTC = 0x0030, // 超长累计定时器 (线圈) (位) LSTN = 0x0031, // 超长累计定时器 (当前值) (双字) SPB = 0x0032, // 缓冲存储器 (字) MAIL = 0x0065, // 特殊软元件类型:邮件类型 (10进制 101) MAILMC = 0x0066, // 特殊软元件类型:无确认邮件 (10进制 102) LX = 0x03E8, // 链接直接软元件 (链接输入) (0x03E9~0x04E7) (位) LY = 0x07D0, // 链接直接软元件 (链接输出) (0x07D1~0x08CF) (位) LB = 0x59D8, // 链接直接软元件 (链接继电器) (0x59D9~0x5AD7) (位) LW = 0x5DC0, // 链接直接软元件 (链接寄存器) (0x5DC1~0x5EBF) (字) LSB = 0x61A8, // 链接直接软元件 (链接特殊继电器) (0x61A9~0x62A7) (位) LSW = 0x6D60, // 链接直接软元件 (链接特殊寄存器) (0x6D61~0x6E5F) (字) SPG = 0x7147, // 智能功能模块软元件 (0x7148~0x7247) (字) X = 0x0001, // 输入 (位) Y = 0x0002, // 输出 (位) L = 0x0003, // 锁存继电器 (位) M = 0x0004, // 内部继电器 (位) SM = 0x0005, // 特殊继电器 (位) F = 0x0006, // 报警器 (位) TT = 0x0007, // 定时器 (触点) (位) TC = 0x0008, // 计数器 (线圈) (位) CT = 0x0009, // 计数器 (触点) (位) CC = 0x000A, // 计数器 (线圈) (字) TN = 0x000B, // 定时器 (当前值) (字) CN = 0x000C, // 计数器 (当前值) (字) D = 0x000D, // 数据寄存器 (字) SD = 0x000E, // 特殊寄存器 (字) TM = 0x000F, // 定时器 (设置值主) (字) TS = 0x0010, // 定时器 (设置值主1) (字) TS2 = 0x3E82, // 定时器 (设置值主2) (字) TS3 = 0x3E83, // 定时器 (设置值主3) (字) CM = 0x0011, // 计数器 (设置值主) (字) CS = 0x0012, // 计数器 (设置值主1) (字) CS2 = 0x4652, // 计数器 (设置值主2) (字) CS3 = 0x4653, // 计数器 (设置值主3) (字) A = 0x0013, // 累加器 (字) Z = 0x0014, // 变址寄存器 (字) V = 0x0015, // 变址寄存器 (字) R = 0x0016, // 文件寄存器 (块切换方式) (字) ER = 0x55F0, // 扩展文件寄存器 (块切换方式) (0x55F0~0x56F0) (字) (在随机读取(mdRandR、mdRandREx)函数中,即使指定实际不存在的软元件也有可能正常结束。(读取数据不正确。)) ZR = 0x00DC, // 文件寄存器 (连号访问方式) (字) B = 0x0017, // 链接继电器 (位) W = 0x0018, // 链接寄存器 (字) QSB = 0x0019, // 链接特殊继电器 (位) STT = 0x001A, // 累计定时器 (触点) (位) STC = 0x001B, // 累计定时器 (线圈) (位) QSW = 0x001C, // 链接特殊寄存器 (字) QV = 0x001E, // 变址继电器 (位) MRB = 0x0021, // 随机访问缓冲 (字) STN = 0x0023, // 累计定时器 (当前值) (字) LZ = 0x0026, // 超长变址寄存器 (双字) RD = 0x0027, // 刷新数据寄存器 (字) LTT = 0x0029, // 超长定时器 (触点) (位) LTC = 0x002A, // 超长定时器 (线圈) (位) LTN = 0x002B, // 超长定时器 (当前值) (双字) LCT = 0x002C, // 超长计数器 (触点) (位) LCC = 0x002D, // 超长计数器 (线圈) (位) LCN = 0x002E, // 超长计数器 (当前值) (双字) LSTT = 0x002F, // 超长累计定时器 (触点) (位) LSTC = 0x0030, // 超长累计定时器 (线圈) (位) LSTN = 0x0031, // 超长累计定时器 (当前值) (双字) SPB = 0x0032, // 缓冲存储器 (字) MAIL = 0x0065, // 特殊软元件类型:邮件类型 (10进制 101) MAILMC = 0x0066, // 特殊软元件类型:无确认邮件 (10进制 102) LX = 0x03E8, // 链接直接软元件 (链接输入) (0x03E9~0x04E7) (位) LY = 0x07D0, // 链接直接软元件 (链接输出) (0x07D1~0x08CF) (位) LB = 0x59D8, // 链接直接软元件 (链接继电器) (0x59D9~0x5AD7) (位) LW = 0x5DC0, // 链接直接软元件 (链接寄存器) (0x5DC1~0x5EBF) (字) LSB = 0x61A8, // 链接直接软元件 (链接特殊继电器) (0x61A9~0x62A7) (位) LSW = 0x6D60, // 链接直接软元件 (链接特殊寄存器) (0x6D61~0x6E5F) (字) SPG = 0x7147, // 智能功能模块软元件 (0x7148~0x7247) (字) }; // 数据类型 // 数据类型 enum class DataType { BIT = 1, // λ (1λ) WORD = 2, // 字 (16位) DWORD = 4 // 双字 (32位) BIT = 1, // 位 (1位) WORD = 2, // 字 (16位) DWORD = 4 // 双字 (32位) }; // 控制代码 // 控制代码 enum class ControlCode { RUN = 0, // 远程 RUN STOP = 1, // 远程 STOP PAUSE = 2 // 远程 PAUSE RUN = 0, // 远程 RUN STOP = 1, // 远程 STOP PAUSE = 2 // 远程 PAUSE }; // 版本信息 // 版本信息 struct BoardVersion { char fixedValue[2]; // 固定值 char checksum[2]; // 校验和 char swVersion[2]; // 软件版本 char date[6]; // 日期 (格式 YYMMDD) uint32_t reserved; // 保留区域 (4 字节) char swModel[16]; // 软件型号 char hwModel[16]; // 硬件型号 char twoPortMemory[2]; // 两端口存储器占用容量 char twoPortAttribute[2]; // 两端口属性 char availableBias[2]; // 可使用偏置 char moduleType[10]; // 机型类型 char fixedValue[2]; // 固定值 char checksum[2]; // 校验和 char swVersion[2]; // 软件版本 char date[6]; // 日期 (格式 YYMMDD) uint32_t reserved; // 保留区域 (4 字节) char swModel[16]; // 软件型号 char hwModel[16]; // 硬件型号 char twoPortMemory[2]; // 两端口存储器占用容量 char twoPortAttribute[2]; // 两端口属性 char availableBias[2]; // 可使用偏置 char moduleType[10]; // 机型类型 // 输出结构体内容为字符串 (便于调试) // 输出结构体内容为字符串 (便于调试) std::string toString() const { std::ostringstream oss; oss << "Fixed Value: " << fixedValue[0] << fixedValue[1] << "\n" @@ -174,18 +174,18 @@ } }; // 站点标识符,默认使用本站 // 站点标识符,默认使用本站 struct StationIdentifier { /* * [Network No.] * 0 表示本站 * 1~239 表示普通网络号 * 0 表示本站 * 1~239 表示普通网络号 **/ /* * [Station No.] * MELSECNET/H:1~64 表示其他站点,255 表示本站 * CC-Link 系列网络的范围类似,区别在于站号的取值范围 * MELSECNET/H:1~64 表示其他站点,255 表示本站 * CC-Link 系列网络的范围类似,区别在于站号的取值范围 * MELSECNET/H : 1~64(Other stations),255(Own station) * CC-Link : 0~63(Other stations),255(Own station) * CC-Link IE Controller : 1~120(Other stations),255(Own station) @@ -194,15 +194,15 @@ **/ /* * 高 8 位(网络号): 指定设备所属的网络 * 低 8 位(站点号): 指定设备在网络中的编号 * 用一个参数传递设备的网络号和站点号时: nSt = station.nStNo | ((station.nNetNo << 8) & 0xFF00); * 高 8 位(网络号): 指定设备所属的网络 * 低 8 位(站点号): 指定设备在网络中的编号 * 用一个参数传递设备的网络号和站点号时: nSt = station.nStNo | ((station.nNetNo << 8) & 0xFF00); **/ short nNetNo = 0; // 网络编号:PLC所连接的网络编号,0表示默认网络 short nStNo = 255; // 站点编号:指定与PLC连接的站点编号,255通常表示广播或所有站点 short nNetNo = 0; // 网络编号:PLC所连接的网络编号,0表示默认网络 short nStNo = 255; // 站点编号:指定与PLC连接的站点编号,255通常表示广播或所有站点 // 自定义构造函数,覆盖默认值 // 自定义构造函数,覆盖默认值 explicit StationIdentifier(const short net, const short st) : nNetNo(net), nStNo(st) {} StationIdentifier() @@ -211,24 +211,24 @@ nStNo = 255; } // 将“网络号”和“站点号”组合成一个最终编码 // 将“网络号”和“站点号”组合成一个最终编码 short StationIdentifier::toNetworkStationCode() const { return static_cast<short>(nStNo | ((nNetNo << 8) & 0xFF00)); } // 重载 < 运算符(用于排序或比较,通常用于 map 或 set 中作为 key) // 重载 < 运算符(用于排序或比较,通常用于 map 或 set 中作为 key) bool operator<(const StationIdentifier& other) const { return std::tie(nNetNo, nStNo) < std::tie(other.nNetNo, other.nStNo); } // 重载 == 运算符(用于相等比较) // 重载 == 运算符(用于相等比较) bool operator==(const StationIdentifier& other) const { return std::tie(nNetNo, nStNo) == std::tie(other.nNetNo, other.nStNo); } // 重载 = 运算符(用于赋值) // 重载 = 运算符(用于赋值) StationIdentifier& operator=(const StationIdentifier& other) { if (this != &other) { nNetNo = other.nNetNo; @@ -238,16 +238,16 @@ } }; // 板状态 // 板状态 struct BoardStatus { short nStationValue = 0; // 站号的设备值 (buf[0]) short nGroupValue = 0; // 组 No. 的设备值 (buf[1]) short nNetworkValue = 0; // 网络 No. 的设备值 (buf[2]) short nReserved1 = 0; // 保留字段 (buf[3]) short nReserved2 = 0; // 保留字段 (buf[4]) short nReserved3 = 0; // 保留字段 (buf[5]) short nStationValue = 0; // 站号的设备值 (buf[0]) short nGroupValue = 0; // 组 No. 的设备值 (buf[1]) short nNetworkValue = 0; // 网络 No. 的设备值 (buf[2]) short nReserved1 = 0; // 保留字段 (buf[3]) short nReserved2 = 0; // 保留字段 (buf[4]) short nReserved3 = 0; // 保留字段 (buf[5]) // 将数组映射到结构体 // 将数组映射到结构体 static BoardStatus fromBuffer(const short buf[6]) { return { buf[0], @@ -259,7 +259,7 @@ }; } // 将结构体内容映射到数组 // 将结构体内容映射到数组 void toBuffer(short buf[6]) const { buf[0] = nStationValue; buf[1] = nGroupValue; @@ -269,7 +269,7 @@ buf[5] = nReserved3; } // 调试输出 // 调试输出 std::string toString() const { std::ostringstream oss; oss << "Station Value: " << nStationValue << "\n" @@ -282,12 +282,12 @@ } }; // 事件详情 // 事件详情 struct EventDetails { short nEventNo; // 发生的事件号 std::array<short, 4> details; // 存储事件详情信息 short nEventNo; // 发生的事件号 std::array<short, 4> details; // 存储事件详情信息 // 解析事件详情,返回格式化字符串 // 解析事件详情,返回格式化字符串 std::string toString() const { std::ostringstream oss; oss << "Details[0]: " << details[0] << ", " @@ -298,33 +298,33 @@ } }; // SoftElement 结构体定义 // SoftElement 结构体定义 struct SoftElement { short nType; // 软元件类型 short nElementCount; // 点数 long nStartNo; // 起始软元件编号 short nType; // 软元件类型 short nElementCount; // 点数 long nStartNo; // 起始软元件编号 }; // 错误信息 // 错误信息 struct ErrorInfo { int nErrorCode = 0; // 错误码 std::string strErrorMessageCn; // 中文描述 std::string strErrorMessageEn; // 英文描述 int nErrorCode = 0; // 错误码 std::string strErrorMessageCn; // 中文描述 std::string strErrorMessageEn; // 英文描述 // 将结构体序列化为字符串 // 将结构体序列化为字符串 std::string toString() const { std::ostringstream oss; oss << nErrorCode << "|" << strErrorMessageCn << "|" << strErrorMessageEn; return oss.str(); } // 从字符串反序列化为结构体 // 从字符串反序列化为结构体 static ErrorInfo fromString(const std::string& line) { ErrorInfo info; std::istringstream iss(line); std::string token; // 使用分隔符 "|" 解析字符串 // 使用分隔符 "|" 解析字符串 std::getline(iss, token, '|'); info.nErrorCode = std::stoi(token); @@ -338,47 +338,47 @@ } }; using BitContainer = std::vector<bool>; // 每个元素存储 1 位 using WordContainer = std::vector<uint16_t>; // 每个元素存储 16 位 using DWordContainer = std::vector<uint32_t>; // 每个元素存储 32 位 using BitContainer = std::vector<bool>; // 每个元素存储 1 位 using WordContainer = std::vector<uint16_t>; // 每个元素存储 16 位 using DWordContainer = std::vector<uint32_t>; // 每个元素存储 32 位 // CPerformanceMelsec 类声明 // CPerformanceMelsec 类声明 class CPerformanceMelsec { public: // 获取最近的错误信息 // 获取最近的错误信息 std::string GetLastError() const; // 错误信息加载与保存接口 static bool LoadErrorInfoFromFile(const std::string& filename); // 从文件加载错误信息 static bool SaveErrorInfoToFile(const std::string& filename); // 保存错误信息到文件 // 错误信息加载与保存接口 static bool LoadErrorInfoFromFile(const std::string& filename); // 从文件加载错误信息 static bool SaveErrorInfoToFile(const std::string& filename); // 保存错误信息到文件 // 连接/断开 // 连接/断开 int Connect(short nChannel, short nMode = -1); int Disconnect(); // 初始化可编程控制器软元件信息表 // 初始化可编程控制器软元件信息表 int InitializeController(); // 获取版本信息 // 获取版本信息 int GetBoardVersion(BoardVersion& version); // 板复位 // 板复位 int BoardReset(); // 板LED读取 // 板LED读取 int ReadBoardLed(std::vector<short>& vecLedBuffer); // 读取目标站点CPU类型 // 读取目标站点CPU类型 int ReadCPUCode(const StationIdentifier& station, short& nCPUCode); // 板模式获取/设置 // 板模式获取/设置 int SetBoardMode(short nMode); int GetBoardMode(short& nMode); // 获取板状态 // 获取板状态 int GetBoardStatus(BoardStatus& status); // 读写数据 // 读写数据 int ReadData(const StationIdentifier& station, long nDevType, long nDevNo, long nSize, std::vector<short>& vecData); int ReadBitData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, short nBitCount, BitContainer& vecData); int ReadWordData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, short nWordCount, WordContainer& vecData); @@ -388,7 +388,7 @@ int WriteWordData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, const WordContainer& vecData); int WriteDWordData(const StationIdentifier& station, DeviceType enDevType, short nDevNo, const DWordContainer& vecData); // 扩展读写数据 // 扩展读写数据 long ReadDataEx(const StationIdentifier& station, long nDevType, long nDevNo, long nSize, std::vector<char>& vecData); long ReadBitDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nBitCount, BitContainer& vecData); long ReadWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, long nWordCount, WordContainer& vecData); @@ -398,56 +398,56 @@ long WriteWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const WordContainer& vecData); long WriteDWordDataEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo, const DWordContainer& vecData); // 扩展软元件随机读写(支持多个软元件) // 扩展软元件随机读写(支持多个软元件) long ReadRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, std::vector<char>& vecData); long WriteRandomDataEx(const StationIdentifier& station, const std::vector<SoftElement>& vecSoftElements, const std::vector<char>& vecData); // 远程设备站/远程站的缓冲存储器读写 // 远程设备站/远程站的缓冲存储器读写 long ReadRemoteBuffer(const StationIdentifier& station, long nOffset, long nSize, std::vector<char>& vecData); long WriteRemoteBuffer(const StationIdentifier& station, long nOffset, const std::vector<char>& vecData); long ReadRemoteBufferByIp(const std::string& strIP, long nOffset, long nSize, std::vector<char>& vecData); long WriteRemoteBufferByIp(const std::string& strIP, long nOffset, const std::vector<char>& vecData); // 设置/复位对象站的指定位软元件 // 设置/复位对象站的指定位软元件 int SetBitDevice(const StationIdentifier& station, DeviceType enDevType, short nDevNo); int ResetBitDevice(const StationIdentifier& station, DeviceType enDevType, short enDevNo); // 扩展设置/复位对象站的指定位软元件 // 扩展设置/复位对象站的指定位软元件 long SetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo); long ResetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo); // 执行对象站的CPU // 执行对象站的CPU int ControlCPU(const StationIdentifier& station, ControlCode enControlCode); // 事件等待,vecEventNumbers[0, 64],nTimeoutMs[-1, 2147483647] // 同时发生了多个事件的情况下,首先检测出其中一个事件。 再次执行了本函数的情况下检测出其它事件。 // 事件等待,vecEventNumbers[0, 64],nTimeoutMs[-1, 2147483647] // 同时发生了多个事件的情况下,首先检测出其中一个事件。 再次执行了本函数的情况下检测出其它事件。 int WaitForBoardEvent(std::vector<short> vecEventNumbers, int nTimeoutMs, EventDetails& details); private: // 锁定与解锁(多线程同步保护) // 锁定与解锁(多线程同步保护) void Lock() { m_mtx.lock(); } void Unlock() { m_mtx.unlock(); } protected: // 构造函数/析构函数 // 构造函数/析构函数 explicit CPerformanceMelsec(BoardType enBoardType); virtual ~CPerformanceMelsec(); // 辅助函数 void UpdateLastError(int nCode); // 更新最近的错误信息 int ValidateStation(const StationIdentifier& station) const; // 检查连接状态和站点参数有效性 // 辅助函数 void UpdateLastError(int nCode); // 更新最近的错误信息 int ValidateStation(const StationIdentifier& station) const; // 检查连接状态和站点参数有效性 int ValidateStationAndSize(const StationIdentifier& station, short nCount) const; // 静态辅助函数 static void Delay(unsigned int nDelayMs); // 延时,并且转发窗口消息 static BoardType FindBoardTypeByChannel(int nChannel); // 查找板块类型 static short CombineStation(const StationIdentifier& station); // 合并网络号和站点号 static short CalculateDeviceType(const StationIdentifier& station, DeviceType enDevType); // 计算软元件类型 // 静态辅助函数 static void Delay(unsigned int nDelayMs); // 延时,并且转发窗口消息 static BoardType FindBoardTypeByChannel(int nChannel); // 查找板块类型 static short CombineStation(const StationIdentifier& station); // 合并网络号和站点号 static short CalculateDeviceType(const StationIdentifier& station, DeviceType enDevType); // 计算软元件类型 // IP转换 // IP转换 static bool ConvertIpStringToUint32(const std::string& strIP, uint32_t& nIP); // 容器转换 // 容器转换 static void ConvertCharToShort(const std::vector<char>& vecChar, std::vector<short>& vecShort); static void ConvertShortToChar(const std::vector<short>& vecShort, std::vector<char>& vecChar); static void ConvertUint8ToShort(const std::vector<uint8_t>& vecUint8, std::vector<short>& vecShort); @@ -455,7 +455,7 @@ static void ConvertUint32ToShort(const std::vector<uint32_t>& vecUint32, std::vector<short>& vecShort); static void ConvertShortToUint32(const std::vector<short>& vecShort, std::vector<uint32_t>& vecUint32); // 模板辅助函数 // 模板辅助函数 template <typename T> int ValidateStationAndData(const StationIdentifier& station, const std::vector<T>& vecData); @@ -465,15 +465,15 @@ template <typename T, typename U> void ConvertHighToLow(const std::vector<T>& vecHigh, std::vector<U>& vecLow); // 成员变量 std::mutex m_mtx; // 互斥锁保护 BoardType m_enBoardType; // 板块类型 long m_nPath; // 通信路径 std::atomic<bool> m_bConnected; // 是否已连接 std::string m_strLastError; // 最近一次错误信息 // 成员变量 std::mutex m_mtx; // 互斥锁保护 BoardType m_enBoardType; // 板块类型 long m_nPath; // 通信路径 std::atomic<bool> m_bConnected; // 是否已连接 std::string m_strLastError; // 最近一次错误信息 // 静态成员变量 static std::unordered_map<int, std::string> m_mapError; // 错误码映射表 // 静态成员变量 static std::unordered_map<int, std::string> m_mapError; // 错误码映射表 }; #endif // PERFORMANCE_MELSEC_H SourceCode/Bond/SGMeasurement/Logger.cpp
SourceCode/Bond/SGMeasurement/Logger.h
@@ -4,14 +4,14 @@ class CLogger { public: static CLogger& Instance(); // 获取单例 void WriteLine(CString str); // 写一行日志 void CloseLogFile(); // 关闭日志文件 static CLogger& Instance(); // 获取单例 void WriteLine(CString str); // 写一行日志 void CloseLogFile(); // 关闭日志文件 private: CLogger(); ~CLogger(); void OpenLogFile(); // 内部打开文件 void OpenLogFile(); // 内部打开文件 CCriticalSection m_csLogLock; CString m_strCurrentLogPath; SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp
@@ -1,39 +1,39 @@ #include "pch.h" #include "PLCSignalListener.h" // === 日志打印类型 === // === 日志打印类型 === #define LOG_TYPE_ERROR -1 #define LOG_TYPE_SUCCESS 0 #define LOG_TYPE_WARNING 1 #define LOG_TYPE_NORMAL 2 // === 日志打印宏定义 === // === 日志打印宏定义 === #define LOG_MSG(msg, type) LogInfo(msg, type) // === PLC 心跳相关配置 === #define PLC_HEARTBEAT_PC_TO_PLC_ADDR 0x107F // PC -> PLC:PC 写入心跳 #define PLC_HEARTBEAT_PLC_TO_PC_ADDR 0x6C40 // PLC -> PC:PC 读取 PLC 写入的心跳 #define MAX_MISSED_HEARTBEAT 5 // 允许连续丢失心跳的最大次数,超过则判定 PLC 掉线 // === PLC 心跳相关配置 === #define PLC_HEARTBEAT_PC_TO_PLC_ADDR 0x107F // PC -> PLC:PC 写入心跳 #define PLC_HEARTBEAT_PLC_TO_PC_ADDR 0x6C40 // PLC -> PC:PC 读取 PLC 写入的心跳 #define MAX_MISSED_HEARTBEAT 5 // 允许连续丢失心跳的最大次数,超过则判定 PLC 掉线 // === PLC 命令输入配置(PLC -> PC) === #define PLC_CMD_BIT_START 0x6CD3 // PLC命令起始位(通常为B6CD3) #define PLC_CMD_BIT_COUNT 2 // 总共几个命令位(B6CD3=Start, B6CD4=Stop) // === PLC 命令输入配置(PLC -> PC) === #define PLC_CMD_BIT_START 0x6CD3 // PLC命令起始位(通常为B6CD3) #define PLC_CMD_BIT_COUNT 2 // 总共几个命令位(B6CD3=Start, B6CD4=Stop) // === PLC 应答输出配置(PC -> PLC) === #define PLC_ACK_MAX_LIFE 25 // PLC响应信号最大保留周期数(每周期为 m_nIntervalMs 毫秒) #define PLC_ACK_BASE_BIT 0x1060 // PLC应答起始地址(B1060表示B6CD3的应答;B1061表示B6CD4的应答) // === PLC 应答输出配置(PC -> PLC) === #define PLC_ACK_MAX_LIFE 25 // PLC响应信号最大保留周期数(每周期为 m_nIntervalMs 毫秒) #define PLC_ACK_BASE_BIT 0x1060 // PLC应答起始地址(B1060表示B6CD3的应答;B1061表示B6CD4的应答) // === PLC软元件类型宏(用于应答、数据写入)=== #define PLC_BIT_DEVICE_TYPE DeviceType::B // 位操作设备类型(如M、B) #define PLC_WORD_DEVICE_TYPE DeviceType::W // 字操作设备类型(如D、W) // === PLC软元件类型宏(用于应答、数据写入)=== #define PLC_BIT_DEVICE_TYPE DeviceType::B // 位操作设备类型(如M、B) #define PLC_WORD_DEVICE_TYPE DeviceType::W // 字操作设备类型(如D、W) // === PLC结果寄存器地址配置 === #define PLC_RESULT_ADDR_START 0x37B0 // PLC结果寄存器起始地址(如W37B0) #define PLC_RESULT_ADDR_COUNT 4 // 结果寄存器数量(如W37B0, W37B2, W37B4, W37B6) // === PLC结果寄存器地址配置 === #define PLC_RESULT_ADDR_START 0x37B0 // PLC结果寄存器起始地址(如W37B0) #define PLC_RESULT_ADDR_COUNT 4 // 结果寄存器数量(如W37B0, W37B2, W37B4, W37B6) // === PLC 产品ID配置(PLC -> PC)=== #define PLC_PRODUCT_ID_ADDR 0x1B160 // 产品ID起始地址 (W1B160) #define PLC_PRODUCT_ID_WORDS 10 // 产品ID长度(10个Word) // === PLC 产品ID配置(PLC -> PC)=== #define PLC_PRODUCT_ID_ADDR 0x1B160 // 产品ID起始地址 (W1B160) #define PLC_PRODUCT_ID_WORDS 10 // 产品ID长度(10个Word) #define IS_RISING_EDGE(prev, curr) (!(prev) && (curr)) @@ -47,7 +47,7 @@ { m_pPlc = std::make_unique<CCCLinkIEControl>(); if (!m_pPlc) { LOG_MSG(_T("PLC控制器初始化失败,无法创建 CCCLinkIEControl 实例。"), LOG_TYPE_ERROR); LOG_MSG(_T("PLC控制器初始化失败,无法创建 CCCLinkIEControl 实例。"), LOG_TYPE_ERROR); return false; } @@ -56,7 +56,7 @@ m_bConnected = false; CString strError; strError.Format(_T("PLC控制器连接失败,错误码:%d"), ret); strError.Format(_T("PLC控制器连接失败,错误码:%d"), ret); LOG_MSG(strError, LOG_TYPE_ERROR); return false; @@ -94,7 +94,7 @@ bool CPLCSignalListener::Start() { if (m_bRunning || !m_pPlc) { LOG_MSG(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR); LOG_MSG(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR); return false; } @@ -164,7 +164,7 @@ if (m_bHeartbeatLost) { m_bHeartbeatLost = false; LOG_MSG(_T("PLC心跳恢复!"), LOG_TYPE_SUCCESS); LOG_MSG(_T("PLC心跳恢复!"), LOG_TYPE_SUCCESS); } return true; @@ -176,7 +176,7 @@ if (!m_bHeartbeatLost) { m_bHeartbeatLost = true; m_nMissedHeartbeatCount = 0; LOG_MSG(_T("PLC心跳信号中断!"), LOG_TYPE_ERROR); LOG_MSG(_T("PLC心跳信号中断!"), LOG_TYPE_ERROR); } return false; } @@ -236,7 +236,7 @@ ::Sleep(m_nIntervalMs); CString strError; strError.Format(_T("PLC读取位数据失败,错误码:%d"), ret); strError.Format(_T("PLC读取位数据失败,错误码:%d"), ret); LOG_MSG(strError, LOG_TYPE_ERROR); continue; @@ -244,7 +244,7 @@ for (int i = 0; i < PLC_CMD_BIT_COUNT; ++i) { if (IS_RISING_EDGE(m_vecPrevBits[i], vecBits[i])) { // 上升沿触发 // 上升沿触发 switch (i) { case 0: if (m_cbStart) { @@ -258,7 +258,7 @@ std::string strProductID; if (ReadProductID(strProductID)) { CString msg; msg.Format(_T("读取到产品ID:%s"), strProductID); msg.Format(_T("读取到产品ID:%s"), strProductID); LOG_MSG(msg, LOG_TYPE_SUCCESS); } } @@ -292,17 +292,17 @@ bool CPLCSignalListener::WriteOutValues(const OutValuesArray& values) { if (!m_pPlc || !m_bConnected) { LOG_MSG(_T("PLC未连接或未初始化,无法写入输出值。"), LOG_TYPE_ERROR); LOG_MSG(_T("PLC未连接或未初始化,无法写入输出值。"), LOG_TYPE_ERROR); return false; } if (PLC_RESULT_ADDR_COUNT != 4) { LOG_MSG(_T("PLC结果寄存器数量配置错误,必须为4个。"), LOG_TYPE_ERROR); LOG_MSG(_T("PLC结果寄存器数量配置错误,必须为4个。"), LOG_TYPE_ERROR); return false; } for (int i = 0; i < PLC_RESULT_ADDR_COUNT; ++i) { // 放大1000倍并四舍五入,转为PLC整数 // 放大1000倍并四舍五入,转为PLC整数 int32_t nScaled = static_cast<int32_t>(std::round(values[i] * 1000.0)); DWordContainer vec = { static_cast<uint32_t>(nScaled) }; @@ -310,7 +310,7 @@ int ret = m_pPlc->WriteDWordDataEx(m_station, PLC_WORD_DEVICE_TYPE, nTargetAddr, vec); if (ret != 0) { CString msg; msg.Format(_T("写入OUT%d到地址%d失败,值=%.2f"), i + 1, nTargetAddr, values[i]); msg.Format(_T("写入OUT%d到地址%d失败,值=%.2f"), i + 1, nTargetAddr, values[i]); LOG_MSG(msg, LOG_TYPE_ERROR); return false; } @@ -322,7 +322,7 @@ bool CPLCSignalListener::ReadProductID(std::string& strProductID) { if (!m_pPlc || !m_bConnected) { LOG_MSG(_T("PLC未连接或未初始化,无法读取产品ID。"), LOG_TYPE_ERROR); LOG_MSG(_T("PLC未连接或未初始化,无法读取产品ID。"), LOG_TYPE_ERROR); return false; } @@ -330,7 +330,7 @@ int ret = m_pPlc->ReadWordDataEx(m_station, PLC_WORD_DEVICE_TYPE, PLC_PRODUCT_ID_ADDR, PLC_PRODUCT_ID_WORDS, vec); if (ret != 0 || vec.size() != PLC_PRODUCT_ID_WORDS) { CString msg; msg.Format(_T("读取产品ID失败,错误码=%d"), ret); msg.Format(_T("读取产品ID失败,错误码=%d"), ret); LOG_MSG(msg, LOG_TYPE_ERROR); return false; } @@ -338,8 +338,8 @@ strProductID.clear(); strProductID.reserve(PLC_PRODUCT_ID_WORDS * 2); for (auto w : vec) { char c1 = static_cast<char>(w & 0xFF); // 低字节 char c2 = static_cast<char>((w >> 8) & 0xFF); // 高字节 char c1 = static_cast<char>(w & 0xFF); // 低字节 char c2 = static_cast<char>((w >> 8) & 0xFF); // 高字节 if (c1 == '\0') { break; SourceCode/Bond/SGMeasurement/PLCSignalListener.h
@@ -19,169 +19,169 @@ ~CPLCSignalListener(); /** * @brief 初始化 PLC 信号监听器。 * @brief 初始化 PLC 信号监听器。 * * @param station 目标 PLC 的站号标识符。 * @param nIntervalMs 轮询周期(单位:毫秒),默认 200ms。 * @return true 初始化成功。 * @return false 初始化失败。 * @param station 目标 PLC 的站号标识符。 * @param nIntervalMs 轮询周期(单位:毫秒),默认 200ms。 * @return true 初始化成功。 * @return false 初始化失败。 */ bool Initialize(StationIdentifier station, int nIntervalMs = 200); /** * @brief 设置开始命令的回调函数(对应 PLC 的 Start 信号)。 * @brief 设置开始命令的回调函数(对应 PLC 的 Start 信号)。 * * @param cb 用户自定义的开始回调函数。 * @param cb 用户自定义的开始回调函数。 */ void SetStartCallback(Callback cb); /** * @brief 设置停止命令的回调函数(对应 PLC 的 Stop 信号)。 * @brief 设置停止命令的回调函数(对应 PLC 的 Stop 信号)。 * * @param cb 用户自定义的停止回调函数。 * @param cb 用户自定义的停止回调函数。 */ void SetStopCallback(Callback cb); /** * @brief 设置分析计算回调函数,在接收到停止命令后调用。 * @brief 设置分析计算回调函数,在接收到停止命令后调用。 * * @param cb 返回计算结果数组(OUT1~OUT4)的函数。 * @param cb 返回计算结果数组(OUT1~OUT4)的函数。 */ void SetAnalyzeCallback(AnalyzeCallback cb); /** * @brief 设置日志输出回调函数。 * @brief 设置日志输出回调函数。 * * @param cb 用户提供的日志输出接口(包含日志内容和日志类型)。 * @param cb 用户提供的日志输出接口(包含日志内容和日志类型)。 */ void SetLogCallback(LogCallback cb); /** * @brief 启动信号监听线程。 * @brief 启动信号监听线程。 * * @return true 启动成功。 * @return false 启动失败(可能已运行或未初始化)。 * @return true 启动成功。 * @return false 启动失败(可能已运行或未初始化)。 */ bool Start(); /** * @brief 停止信号监听线程。 * @brief 停止信号监听线程。 */ void Stop(); /** * @brief 向 PLC 写入分析结果值(通常为 OUT1 ~ OUT4)。 * @brief 向 PLC 写入分析结果值(通常为 OUT1 ~ OUT4)。 * * @param values 包含四个 double 类型的结果值,将转换为整数后写入 PLC。 * @return true 写入成功。 * @return false 写入失败。 * @param values 包含四个 double 类型的结果值,将转换为整数后写入 PLC。 * @return true 写入成功。 * @return false 写入失败。 */ bool WriteOutValues(const OutValuesArray& values); /** * @brief 读取 PLC 内部的产品 ID(字符串形式)。 * @brief 读取 PLC 内部的产品 ID(字符串形式)。 * * @param strProductID 输出参数,存储读取到的产品 ID。 * @return true 读取成功。 * @return false 读取失败。 * @param strProductID 输出参数,存储读取到的产品 ID。 * @return true 读取成功。 * @return false 读取失败。 */ bool ReadProductID(std::string& strProductID); private: /** * @brief 输出日志信息(封装日志回调)。 * @brief 输出日志信息(封装日志回调)。 * * @param strText 日志内容文本。 * @param nType 日志类型(LOG_TYPE_NORMAL / ERROR / WARNING 等)。 * @param strText 日志内容文本。 * @param nType 日志类型(LOG_TYPE_NORMAL / ERROR / WARNING 等)。 */ void LogInfo(const CString& strText, int nType); /** * @brief 向 PLC 写入心跳信号(交替位)。 * @brief 向 PLC 写入心跳信号(交替位)。 * * @return true 写入成功。 * @return false 写入失败(如未连接)。 * @return true 写入成功。 * @return false 写入失败(如未连接)。 */ bool SendHeartbeat(); /** * @brief 检查从 PLC 读取到的心跳信号是否发生变化。 * @brief 检查从 PLC 读取到的心跳信号是否发生变化。 * * @return true 心跳有变化(PLC 正常在线)。 * @return false 心跳无变化(可能离线)。 * @return true 心跳有变化(PLC 正常在线)。 * @return false 心跳无变化(可能离线)。 */ bool CheckHeartbeat(); /** * @brief 监控 PLC 心跳是否中断,并自动记录状态。 * @brief 监控 PLC 心跳是否中断,并自动记录状态。 * * @return true PLC 在线或在允许的未响应次数内。 * @return false PLC 心跳中断,超过允许阈值。 * @return true PLC 在线或在允许的未响应次数内。 * @return false PLC 心跳中断,超过允许阈值。 */ bool MonitorHeartbeat(); /** * @brief 启动心跳监控线程(独立于信号监听线程)。 * @brief 启动心跳监控线程(独立于信号监听线程)。 */ void StartHeartbeatMonitor(); /** * @brief 停止心跳监控线程。 * @brief 停止心跳监控线程。 */ void StopHeartbeatMonitor(); /** * @brief 向指定软元件位写入一个脉冲(先写1,延时后自动写0)。 * @brief 向指定软元件位写入一个脉冲(先写1,延时后自动写0)。 * * @param eDevType 位软元件类型(如 M/B)。 * @param nBitNo 位地址编号。 * @param nDelayMs 脉冲持续时间,默认50毫秒。 * @param eDevType 位软元件类型(如 M/B)。 * @param nBitNo 位地址编号。 * @param nDelayMs 脉冲持续时间,默认50毫秒。 */ void PulseBitDevice(DeviceType eDevType, long nBitNo, int nDelayMs = 50); /** * @brief 管理 ACK 响应的生命周期,超时自动清除。 * @brief 管理 ACK 响应的生命周期,超时自动清除。 * * @param i 控制位索引(0=Start,1=Stop)。 * @param bCurrTriggerBit 当前从 PLC 读取到的触发位状态。 * @param i 控制位索引(0=Start,1=Stop)。 * @param bCurrTriggerBit 当前从 PLC 读取到的触发位状态。 */ void HandleAckLife(int i, bool bCurrTriggerBit); /** * @brief 主监听线程逻辑,循环读取 PLC 控制信号并处理触发。 * @brief 主监听线程逻辑,循环读取 PLC 控制信号并处理触发。 */ void ThreadProc(); private: // === PLC 通信核心对象 === std::unique_ptr<CCCLinkIEControl> m_pPlc; // PLC 通信控制器 StationIdentifier m_station; // PLC 站号 std::atomic<bool> m_bConnected{ false }; // 是否成功连接 // === PLC 通信核心对象 === std::unique_ptr<CCCLinkIEControl> m_pPlc; // PLC 通信控制器 StationIdentifier m_station; // PLC 站号 std::atomic<bool> m_bConnected{ false }; // 是否成功连接 // === 控制参数 === int m_nIntervalMs = 200; // 轮询周期(ms) // === 控制参数 === int m_nIntervalMs = 200; // 轮询周期(ms) // === 命令触发状态缓存 === std::vector<bool> m_vecPrevBits; // 上一周期的命令位状态(用于检测上升沿) // === 命令触发状态缓存 === std::vector<bool> m_vecPrevBits; // 上一周期的命令位状态(用于检测上升沿) // === 回调函数(控制/计算/日志)=== Callback m_cbStart; // Start 命令回调 Callback m_cbStop; // Stop 命令回调 AnalyzeCallback m_cbAnalyze; // Analyze 计算回调(返回 OUT1~OUT4) LogCallback m_cbLog; // 日志输出回调 // === 回调函数(控制/计算/日志)=== Callback m_cbStart; // Start 命令回调 Callback m_cbStop; // Stop 命令回调 AnalyzeCallback m_cbAnalyze; // Analyze 计算回调(返回 OUT1~OUT4) LogCallback m_cbLog; // 日志输出回调 // === 主线程控制 === std::atomic<bool> m_bRunning{ false }; // 主监听线程是否运行 std::thread m_thread; // 主监听线程对象 // === 主线程控制 === std::atomic<bool> m_bRunning{ false }; // 主监听线程是否运行 std::thread m_thread; // 主监听线程对象 // === ACK 信号状态 === std::array<bool, 2> m_vecAckSent = { false, false }; // 是否已发送应答信号(B10/B11) std::array<int, 2> m_vecAckCounter = { 0, 0 }; // 对应应答信号的生命周期计数器 // === ACK 信号状态 === std::array<bool, 2> m_vecAckSent = { false, false }; // 是否已发送应答信号(B10/B11) std::array<int, 2> m_vecAckCounter = { 0, 0 }; // 对应应答信号的生命周期计数器 // === 心跳检测相关 === std::thread m_heartbeatThread; // 心跳检测线程对象 std::atomic<bool> m_bHeartbeatRunning = false; // 心跳线程运行标志 std::atomic<bool> m_bHeartbeatLost = false; // 心跳丢失标志 int m_nMissedHeartbeatCount = 0; // 心跳未变化次数(用于检测 PLC 掉线) // === 心跳检测相关 === std::thread m_heartbeatThread; // 心跳检测线程对象 std::atomic<bool> m_bHeartbeatRunning = false; // 心跳线程运行标志 std::atomic<bool> m_bHeartbeatLost = false; // 心跳丢失标志 int m_nMissedHeartbeatCount = 0; // 心跳未变化次数(用于检测 PLC 掉线) }; SourceCode/Bond/SGMeasurement/SGMeasurement.cpp
@@ -1,4 +1,4 @@ // SGMeasurement.cpp: 定义应用程序的类行为。 // SourceCode/Bond/SGMeasurement/SGMeasurement.h
@@ -1,4 +1,4 @@ // SGMeasurement.h: PROJECT_NAME 应用程序的主头文件 // SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp
@@ -1,4 +1,4 @@ // SGMeasurementDlg.cpp: 实现文件 // SourceCode/Bond/SGMeasurement/SGMeasurementDlg.h
@@ -1,4 +1,4 @@ // SGMeasurementDlg.h: 头文件 // SourceCode/Bond/SGMeasurement/framework.h
@@ -1,4 +1,4 @@ #pragma once #pragma once #ifndef VC_EXTRALEAN #define VC_EXTRALEAN // 从 Windows 头中排除极少使用的资料 SourceCode/Bond/SGMeasurement/pch.cpp
@@ -1,4 +1,4 @@ // pch.cpp: 与预编译标头对应的源文件 // pch.cpp: 与预编译标头对应的源文件 #include "pch.h" SourceCode/Bond/SGMeasurement/pch.h
@@ -1,4 +1,4 @@ // pch.h: 这是预编译标头文件。 // pch.h: 这是预编译标头文件。 // 下方列出的文件仅编译一次,提高了将来生成的生成性能。 // 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 // 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 SourceCode/Bond/SGMeasurement/resource.h
@@ -1,4 +1,4 @@ //{{NO_DEPENDENCIES}} //{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 SGMeasurement.rc 使用 // SourceCode/Bond/SGMeasurement/targetver.h
@@ -1,4 +1,4 @@ #pragma once #pragma once // 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。