| SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/SGMeasurement/PLCSignalListener.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/SGMeasurement/SGMeasurement.vcxproj | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.cpp
@@ -703,18 +703,19 @@ return nRet; } if (nDevNo % 8 != 0) { UpdateLastError(ERROR_CODE_INVALID_PARAM); return ERROR_CODE_INVALID_PARAM; } const short nDevType = CalculateDeviceType(station, enDevType); const long nWordCount = (nBitCount + 15) / 16; const long nByteSize = nWordCount * sizeof(short); // === 自动对齐到起始字 === long nWordAlignedStartBit = nDevNo / 16 * 16; long nBitOffset = nDevNo - nWordAlignedStartBit; long nTotalBits = nBitOffset + nBitCount; long nWordCount = (nTotalBits + 15) / 16; long nByteSize = nWordCount * sizeof(short); std::vector<char> vecRaw; nRet = ReadDataEx(station, nDevType, nDevNo, nByteSize, vecRaw); nRet = ReadDataEx(station, nDevType, nWordAlignedStartBit, nByteSize, vecRaw); if (nRet != 0) { UpdateLastError(nRet); return nRet; } @@ -722,10 +723,11 @@ for (long i = 0; i < nWordCount; ++i) { short word = static_cast<unsigned char>(vecRaw[i * 2]) | (static_cast<unsigned char>(vecRaw[i * 2 + 1]) << 8); for (int j = 0; j < 16; ++j) { long bitIndex = i * 16 + j; if (bitIndex >= nBitOffset && vecData.size() < static_cast<size_t>(nBitCount)) { vecData.push_back((word & (1 << j)) != 0); if (vecData.size() >= static_cast<size_t>(nBitCount)) { return 0; } } } @@ -826,30 +828,48 @@ return nRet; } if (nDevNo % 8 != 0) { UpdateLastError(ERROR_CODE_INVALID_PARAM); return ERROR_CODE_INVALID_PARAM; } const short nDevType = CalculateDeviceType(station, enDevType); const size_t nWordCount = (vecData.size() + 15) / 16; // === 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. 先读取原始值以支持非对齐覆盖 === std::vector<char> vecRaw; nRet = ReadDataEx(station, nDevType, nWordAlignedStartBit, static_cast<long>(nWordCount * sizeof(short)), vecRaw); if (nRet != 0) { UpdateLastError(nRet); return nRet; } // === 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); } for (size_t i = 0; i < vecData.size(); ++i) { size_t bitIndex = nBitOffset + i; size_t wordIdx = bitIndex / 16; size_t bitPos = bitIndex % 16; if (vecData[i]) { vecWordBuffer[i / 16] |= (1 << (i % 16)); vecWordBuffer[wordIdx] |= (1 << bitPos); } else { vecWordBuffer[wordIdx] &= ~(1 << bitPos); } } // 转换 short -> char std::vector<char> vecByteBuffer; vecByteBuffer.resize(nWordCount * sizeof(short)); // === 4. 转为字节流写入 === std::vector<char> vecByteBuffer(nWordCount * 2); for (size_t i = 0; i < nWordCount; ++i) { vecByteBuffer[i * 2] = static_cast<char>(vecWordBuffer[i] & 0xFF); vecByteBuffer[i * 2 + 1] = static_cast<char>((vecWordBuffer[i] >> 8) & 0xFF); } return WriteDataEx(station, nDevType, nDevNo, vecByteBuffer); return WriteDataEx(station, nDevType, nWordAlignedStartBit, vecByteBuffer); } // 扩展写字数据 @@ -1146,7 +1166,7 @@ } // 扩展位软元件设置 long CPerformanceMelsec::SetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo) { long CPerformanceMelsec::SetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo) { std::lock_guard<std::mutex> lock(m_mtx); // 检查参数有效性 @@ -1156,6 +1176,7 @@ return nRet; } long nDevType = static_cast<long>(enDevType); nRet = mdDevSetEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo); if (nRet != 0) { UpdateLastError(nRet); @@ -1166,7 +1187,7 @@ } // 扩展位软元件复位 long CPerformanceMelsec::ResetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo) { long CPerformanceMelsec::ResetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo) { std::lock_guard<std::mutex> lock(m_mtx); // 检查参数有效性 @@ -1176,6 +1197,7 @@ return nRet; } long nDevType = static_cast<long>(enDevType); nRet = mdDevRstEx(m_nPath, station.nNetNo, station.nStNo, nDevType, nDevNo); if (nRet != 0) { UpdateLastError(nRet); SourceCode/Bond/SGMeasurement/CCLinkPerformance/PerformanceMelsec.h
@@ -413,8 +413,8 @@ int ResetBitDevice(const StationIdentifier& station, DeviceType enDevType, short enDevNo); // 扩展设置/复位对象站的指定位软元件 long SetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo); long ResetBitDeviceEx(const StationIdentifier& station, long nDevType, long nDevNo); long SetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo); long ResetBitDeviceEx(const StationIdentifier& station, DeviceType enDevType, long nDevNo); // 执行对象站的CPU int ControlCPU(const StationIdentifier& station, ControlCode enControlCode); SourceCode/Bond/SGMeasurement/PLCSignalListener.cpp
@@ -7,17 +7,29 @@ #define LOG_TYPE_WARNING 1 #define LOG_TYPE_NORMAL 2 // === PLC 控制命令输入位配置 === #define PLC_CMD_BIT_START 0 // PLC命令起始位(通常为B0) #define PLC_CMD_BIT_COUNT 2 // 总共几个命令位(B0=Start, B1=Stop) // === 日志打印宏定义 === #define LOG_MSG(msg, type) LogInfo(msg, type) // === 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 应答输出配置(PC -> PLC) === #define PLC_ACK_MAX_LIFE 5 // PLC响应信号最大保留周期数(每周期为 m_nIntervalMs 毫秒) #define PLC_ACK_BASE_BIT 10 // PLC应答起始地址(B10表示B0的应答;B11表示B1) #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_RESULT_ADDR_START 0x37B0 // PLC结果寄存器起始地址(如W37B0) #define PLC_RESULT_ADDR_COUNT 4 // 结果寄存器数量(如W37B0, W37B2, W37B4, W37B6) #define IS_RISING_EDGE(prev, curr) (!(prev) && (curr)) @@ -31,20 +43,18 @@ { m_pPlc = std::make_unique<CCCLinkIEControl>(); if (!m_pPlc) { if (m_cbLog) { m_cbLog(_T("PLC控制器初始化失败,无法创建 CCCLinkIEControl 实例。"), LOG_TYPE_ERROR); } LOG_MSG(_T("PLC控制器初始化失败,无法创建 CCCLinkIEControl 实例。"), LOG_TYPE_ERROR); return false; } int ret = m_pPlc->Connect(CC_LINK_IE_CONTROL_CHANNEL(1)); if (ret != 0) { m_bConnected = false; if (m_cbLog) { CString strError; strError.Format(_T("PLC控制器连接失败,错误码:%d"), ret); m_cbLog(strError, LOG_TYPE_ERROR); } LOG_MSG(strError, LOG_TYPE_ERROR); return false; } @@ -80,14 +90,14 @@ bool CPLCSignalListener::Start() { if (m_bRunning || !m_pPlc) { if (m_cbLog) { m_cbLog(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR); } LOG_MSG(_T("PLC信号监听器已在运行或PLC控制器未初始化。"), LOG_TYPE_ERROR); return false; } m_bRunning = true; m_thread = std::thread(&CPLCSignalListener::ThreadProc, this); StartHeartbeatMonitor(); return true; } @@ -97,25 +107,117 @@ if (m_thread.joinable()) { m_thread.join(); } StopHeartbeatMonitor(); } void CPLCSignalListener::PulseBitDevice(DeviceType eDevType, short nBitNo, int nDelayMs/* = 50*/) void CPLCSignalListener::LogInfo(const CString& strText, int nType) { m_pPlc->SetBitDevice(m_station, eDevType, nBitNo); if (m_cbLog) { m_cbLog(strText, nType); } } bool CPLCSignalListener::SendHeartbeat() { if (!m_pPlc || !m_bConnected) { return false; } static bool bToggle = false; bToggle = !bToggle; int ret = m_pPlc->WriteBitDataEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_HEARTBEAT_PC_TO_PLC_ADDR, BitContainer{ bToggle }); return (ret == 0); } bool CPLCSignalListener::CheckHeartbeat() { static bool bLastHeartbeat = false; if (!m_pPlc || !m_bConnected) { return false; } BitContainer vec; int ret = m_pPlc->ReadBitDataEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_HEARTBEAT_PLC_TO_PC_ADDR, 1, vec); if (ret != 0 || vec.empty()) { return false; } bool bCurrent = vec[0]; bool bChanged = (bCurrent != bLastHeartbeat); bLastHeartbeat = bCurrent; return bChanged; } bool CPLCSignalListener::MonitorHeartbeat() { if (CheckHeartbeat()) { m_nMissedHeartbeatCount = 0; if (m_bHeartbeatLost) { m_bHeartbeatLost = false; LOG_MSG(_T("PLC心跳恢复!"), LOG_TYPE_SUCCESS); } return true; } else { m_nMissedHeartbeatCount++; if (m_nMissedHeartbeatCount > MAX_MISSED_HEARTBEAT) { if (!m_bHeartbeatLost) { m_bHeartbeatLost = true; m_nMissedHeartbeatCount = 0; LOG_MSG(_T("PLC心跳信号中断!"), LOG_TYPE_ERROR); } return false; } } return true; } void CPLCSignalListener::StartHeartbeatMonitor() { m_bHeartbeatRunning = true; m_heartbeatThread = std::thread([this]() { while (m_bHeartbeatRunning) { SendHeartbeat(); MonitorHeartbeat(); std::this_thread::sleep_for(std::chrono::milliseconds(m_nIntervalMs * 5)); } }); } void CPLCSignalListener::StopHeartbeatMonitor() { m_bHeartbeatRunning = false; if (m_heartbeatThread.joinable()) { m_heartbeatThread.join(); } } void CPLCSignalListener::PulseBitDevice(DeviceType eDevType, long nBitNo, int nDelayMs/* = 50*/) { m_pPlc->SetBitDeviceEx(m_station, eDevType, nBitNo); ::Sleep(nDelayMs); m_pPlc->ResetBitDevice(m_station, eDevType, nBitNo); m_pPlc->ResetBitDeviceEx(m_station, eDevType, nBitNo); } void CPLCSignalListener::HandleAckLife(int i, bool bCurrTriggerBit) { if (m_vecAckSent[i] && !bCurrTriggerBit) { m_pPlc->ResetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, short(PLC_ACK_BASE_BIT + i)); m_pPlc->ResetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, long(PLC_ACK_BASE_BIT + i)); m_vecAckSent[i] = false; } if (m_vecAckSent[i]) { if (++m_vecAckCounter[i] > PLC_ACK_MAX_LIFE) { m_pPlc->ResetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, short(PLC_ACK_BASE_BIT + i)); m_pPlc->ResetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, long(PLC_ACK_BASE_BIT + i)); m_vecAckSent[i] = false; } } @@ -123,17 +225,15 @@ void CPLCSignalListener::ThreadProc() { while (m_bRunning) { while (m_bRunning && m_bConnected) { BitContainer vecBits; int ret = m_pPlc->ReadBitData(m_station, PLC_BIT_DEVICE_TYPE, PLC_CMD_BIT_START, PLC_CMD_BIT_COUNT, vecBits); if (ret != 0/*&& vecBits.size() != PLC_CMD_BIT_COUNT*/) { int ret = m_pPlc->ReadBitDataEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_CMD_BIT_START, PLC_CMD_BIT_COUNT, vecBits); if (ret != 0 && vecBits.size() != PLC_CMD_BIT_COUNT) { ::Sleep(m_nIntervalMs); if (m_cbLog) { CString strError; strError.Format(_T("PLC读取位数据失败,错误码:%d"), ret); m_cbLog(strError, LOG_TYPE_ERROR); } LOG_MSG(strError, LOG_TYPE_ERROR); continue; } @@ -146,7 +246,7 @@ if (m_cbStart) { m_cbStart(); WriteOutValues(OutValuesArray{ 0.0, 0.0, 0.0, 0.0 }); if (m_pPlc->SetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) { if (m_pPlc->SetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) { m_vecAckSent[i] = true; m_vecAckCounter[i] = 0; } @@ -156,7 +256,7 @@ case 1: if (m_cbStop) { m_cbStop(); if (m_pPlc->SetBitDevice(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) { if (m_pPlc->SetBitDeviceEx(m_station, PLC_BIT_DEVICE_TYPE, PLC_ACK_BASE_BIT + i) == 0) { m_vecAckSent[i] = true; m_vecAckCounter[i] = 0; } @@ -180,21 +280,27 @@ bool CPLCSignalListener::WriteOutValues(const OutValuesArray& values) { if (!m_pPlc) { if (m_cbLog) { m_cbLog(_T("PLC控制器未初始化,无法写入输出值。"), LOG_TYPE_ERROR); } if (!m_pPlc || !m_bConnected) { LOG_MSG(_T("PLC未连接或未初始化,无法写入输出值。"), LOG_TYPE_ERROR); return false; } static const short PLC_RESULT_ADDR[4] = { 100, 102, 104, 106 }; if (PLC_RESULT_ADDR_COUNT != 4) { LOG_MSG(_T("PLC结果寄存器数量配置错误,必须为4个。"), LOG_TYPE_ERROR); return false; } for (int i = 0; i < 4; ++i) { for (int i = 0; i < PLC_RESULT_ADDR_COUNT; ++i) { // 放大100倍并四舍五入,转为PLC整数 uint16_t nScaled = static_cast<uint16_t>(std::round(values[i] * 100.0)); WordContainer vec = { nScaled }; int ret = m_pPlc->WriteWordData(m_station, PLC_WORD_DEVICE_TYPE, PLC_RESULT_ADDR[i], vec); short nTargetAddr = PLC_RESULT_ADDR_START + i * 2; int ret = m_pPlc->WriteWordDataEx(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]); LOG_MSG(msg, LOG_TYPE_ERROR); return false; } } SourceCode/Bond/SGMeasurement/PLCSignalListener.h
@@ -31,26 +31,46 @@ bool WriteOutValues(const OutValuesArray& values); private: void PulseBitDevice(DeviceType eDevType, short nBitNo, int nDelayMs = 50); void LogInfo(const CString& strText, int nType); bool SendHeartbeat(); bool CheckHeartbeat(); bool MonitorHeartbeat(); void StartHeartbeatMonitor(); void StopHeartbeatMonitor(); void PulseBitDevice(DeviceType eDevType, long nBitNo, int nDelayMs = 50); void HandleAckLife(int i, bool bCurrTriggerBit); void ThreadProc(); private: std::unique_ptr<CCCLinkIEControl> m_pPlc; StationIdentifier m_station; int m_nIntervalMs = 200; // === PLC 通信核心对象 === std::unique_ptr<CCCLinkIEControl> m_pPlc; // PLC 通信控制器 StationIdentifier m_station; // PLC 站号 std::atomic<bool> m_bConnected{ false }; // 是否成功连接 std::atomic<bool> m_bConnected{ false }; std::atomic<bool> m_bRunning{ false }; std::thread m_thread; std::vector<bool> m_vecPrevBits; // === 控制参数 === int m_nIntervalMs = 200; // 轮询周期(ms) std::array<bool, 2> m_vecAckSent = { false, false }; // 是否已发送 M10/M11 std::array<int, 2> m_vecAckCounter = { 0, 0 }; // 计数器,超时自动清除 // === 命令触发状态缓存 === std::vector<bool> m_vecPrevBits; // 上一周期的命令位状态(用于检测上升沿) Callback m_cbStart; Callback m_cbStop; AnalyzeCallback m_cbAnalyze; 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; // 主监听线程对象 // === 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 掉线) }; SourceCode/Bond/SGMeasurement/SGMeasurement.vcxproj
@@ -175,6 +175,7 @@ <PreprocessorDefinitions>_WINDOWS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <AdditionalIncludeDirectories>.;..;.\DLL\64bit;.\CCLinkPerformance;..\MELSECSDK\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <Optimization>MaxSpeed</Optimization> </ClCompile> <Link> <SubSystem>Windows</SubSystem> SourceCode/Bond/SGMeasurement/SGMeasurementDlg.cpp
@@ -887,13 +887,13 @@ if (type == -1) { AppendLogLineRichStyled(msg, LOG_COLOR_ERROR); } if (type == 0) { else if (type == 0) { AppendLogLineRichStyled(msg, LOG_COLOR_SUCCESS); } else if (type == 1) { AppendLogLineRichStyled(msg, LOG_COLOR_WARNING); } else { else if (type == 2) { AppendLogLineRichStyled(msg, LOG_COLOR_NORMAL); } }); @@ -903,9 +903,14 @@ // 设置 PLC 监听器的开始采集回调函数 m_plcListener.SetStartCallback([this]() { InitDataStorage(); if (!m_bConnected) { ConnectToDevice(); } if (InitDataStorage()) { StartDataStorage(); UpdateControlStatus(m_bConnected, m_bSaving); } }); // 设置 PLC 监听器的停止采集回调函数 @@ -916,6 +921,11 @@ // 设置 PLC 监听器的分析回调函数 m_plcListener.SetAnalyzeCallback([this]() { if (!m_bConnected) { AppendLogLineRichStyled(_T("设备未连接,请先连接设备。"), LOG_COLOR_WARNING); return std::array<double, 4>{ -1.0, -1.0, -1.0, -1.0 }; } std::array<double, 4> result; for (int i = 0; i < 4; ++i) { result[i] = AnalyzeStoredData(i + 1); // OUT1 ~ OUT4