| | |
| | | return m_pChannel->writeWord(softComponent, addr, value, funOnWrite); |
| | | } |
| | | |
| | | int CPLC::writeDWord(MC::SOFT_COMPONENT softComponent, unsigned int addr, |
| | | int value, ONWRITE funOnWrite) |
| | | { |
| | | return m_pChannel->writeDWord(softComponent, addr, value, funOnWrite); |
| | | } |
| | | |
| | | int CPLC::writeData(MC::SOFT_COMPONENT softComponent, unsigned int addr, |
| | | const char* pszData, unsigned int length, ONWRITE funOnWrite) |
| | | { |
| | |
| | | int readWord(MC::SOFT_COMPONENT softComponent, unsigned int addr, ONREAD funOnRead); |
| | | int readData(MC::SOFT_COMPONENT softComponent, unsigned int addr, unsigned int nReadLen, ONREADDATA funOnReadData); |
| | | int writeWord(MC::SOFT_COMPONENT softComponent, unsigned int addr, int value, ONWRITE funOnWrite); |
| | | int writeDWord(MC::SOFT_COMPONENT softComponent, unsigned int addr, int value, ONWRITE funOnWrite); |
| | | int writeBit(MC::SOFT_COMPONENT softComponent, unsigned int addr, BOOL bValue, ONWRITE funOnWrite); |
| | | int writeData(MC::SOFT_COMPONENT softComponent, unsigned int addr, const char* pszData, unsigned int length, ONWRITE funOnWrite); |
| | | |
| | |
| | | m_machines[machineName] = data; |
| | | } |
| | | |
| | | |
| | | void IOManager::addDefaultMachineData(const std::string& machineName) { |
| | | /* |
| | | if (machineName == "Cavity") { |
| | | std::vector<IOData> defaultData = { |
| | | {"X1000", "Unit1_急停 EMO", "Y1010", "Unit1_四色灯-红"}, |
| | |
| | | }; |
| | | m_machines[machineName] = defaultData; |
| | | } |
| | | */ |
| | | } |
| | | |
| | | void IOManager::saveToFile(const std::string& machineName) { |
| | |
| | | IOData entry; |
| | | size_t pos = 0; |
| | | pos = line.find(","); |
| | | |
| | | entry.bInputStates = FALSE; |
| | | entry.bOutputStates = FALSE; |
| | | |
| | | entry.inputAddress = line.substr(0, pos); |
| | | line.erase(0, pos + 1); |
| | | |
| | |
| | | #include <map> |
| | | |
| | | struct IOData { |
| | | BOOL bInputStates; |
| | | std::string inputAddress; |
| | | std::string inputDescription; |
| | | BOOL bOutputStates; |
| | | std::string outputAddress; |
| | | std::string outputDescription; |
| | | }; |
| | |
| | | axisInfo.id = axisId; |
| | | axisInfo.number = "M100-M" + std::to_string(axisId); |
| | | axisInfo.description = "Default_Axis" + std::to_string(axisId); |
| | | axisInfo.startAddress = "D" + std::to_string(5000 + axisId * 10); |
| | | axisInfo.startAddress = "ZR" + std::to_string(10000 + (axisId - 1) * 300); |
| | | axisInfo.jogDistance = 0.5; |
| | | axisInfo.manualSpeed = 10.0; |
| | | axisInfo.autoSpeed = 15.0; |
| | |
| | | return (pBuffer[0] & 0xff) | (pBuffer[1] & 0xff) << 8; |
| | | } |
| | | |
| | | BOOL CToolUnits::getBit(const char c, int index) |
| | | { |
| | | switch (index) |
| | | { |
| | | case 0: |
| | | return c & 0x01; |
| | | break; |
| | | case 1: |
| | | return c & 0x02; |
| | | break; |
| | | case 2: |
| | | return c & 0x04; |
| | | break; |
| | | case 3: |
| | | return c & 0x08; |
| | | break; |
| | | case 4: |
| | | return c & 0x10; |
| | | break; |
| | | case 5: |
| | | return c & 0x20; |
| | | break; |
| | | case 6: |
| | | return c & 0x40; |
| | | break; |
| | | case 7: |
| | | return c & 0x80; |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | return FALSE; |
| | | } |
| | | |
| | | void CToolUnits::setDlgItemDouble(CWnd* pWnd, int nCtrlId, double value) |
| | | { |
| | | CString strText; |
| | |
| | | static bool isDirectory(const std::string& path); |
| | | static double toInt32(const char* pBuffer); |
| | | static double toInt16(const char* pBuffer); |
| | | static BOOL getBit(const char c, int index); |
| | | static void setDlgItemDouble(CWnd* pWnd, int nCtrlId, double value); |
| | | }; |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | bool CAxisSettingsDlg::ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr) |
| | | { |
| | | if (address.GetLength() < 2) { |
| | | return false; |
| | | } |
| | | |
| | | // 提取组件类型(第一个字符) |
| | | TCHAR componentChar = address[0]; |
| | | if (address.Left(2) == _T("ZR")) { |
| | | component = MC::SOFT_COMPONENT::ZR; |
| | | // 提取数字部分(去除ZR前缀) |
| | | CString numericAddress = address.Mid(2); |
| | | addr = _ttoi(numericAddress); |
| | | return addr != 0 || numericAddress.CompareNoCase(_T("0")) == 0; // 如果是 "0",也认为有效 |
| | | } |
| | | |
| | | // 对于其他组件,按照常规规则处理 |
| | | CString hexAddress = address.Mid(1); |
| | | switch (componentChar) { |
| | | case 'D': |
| | | component = MC::SOFT_COMPONENT::D; |
| | | addr = _ttoi(hexAddress); |
| | | break; |
| | | case 'M': |
| | | component = MC::SOFT_COMPONENT::M; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'X': |
| | | component = MC::SOFT_COMPONENT::X; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'Y': |
| | | component = MC::SOFT_COMPONENT::Y; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'W': |
| | | component = MC::SOFT_COMPONENT::W; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'L': |
| | | component = MC::SOFT_COMPONENT::L; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'S': |
| | | component = MC::SOFT_COMPONENT::S; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'B': |
| | | component = MC::SOFT_COMPONENT::B; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'F': |
| | | component = MC::SOFT_COMPONENT::F; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | default: |
| | | return false; |
| | | } |
| | | |
| | | // 检查地址是否有效 |
| | | if (addr == 0 && hexAddress.CompareNoCase(_T("0")) != 0) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void CAxisSettingsDlg::writeAxisDataToPLC(int nAxisId) |
| | | { |
| | | // 从 RecipeManager 获取轴数据 |
| | | RecipeManager& recipeManager = RecipeManager::getInstance(); |
| | | auto axisData = recipeManager.getAxis(nAxisId); |
| | | |
| | | // 去除非数字字符并转换起始地址 |
| | | std::string cleanAddress = axisData.startAddress; |
| | | cleanAddress.erase(std::remove_if(cleanAddress.begin(), cleanAddress.end(), |
| | | [](char c) { return !std::isdigit(c); }), cleanAddress.end()); |
| | | if (cleanAddress.empty()) { |
| | | int startAddress; |
| | | MC::SOFT_COMPONENT component; |
| | | if (!ParsePLCAddress(CString(axisData.startAddress.c_str()), component, startAddress)) { |
| | | AfxMessageBox(_T("无效的起始地址!")); |
| | | return; |
| | | } |
| | | int startAddress = std::stoi(cleanAddress); |
| | | |
| | | // 写入手动速度 |
| | | m_pPLC->writeWord(MC::SOFT_COMPONENT::D, 5120, (int)axisData.manualSpeed, [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | m_pPLC->writeDWord(component, startAddress + 82, (int)axisData.manualSpeed * 1000, [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("\n写入成功: 手动速度, 地址: %d, 值: %lu\n", addr, value); |
| | | } |
| | |
| | | }); |
| | | |
| | | // 写入自动速度 |
| | | m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 2, (int)axisData.autoSpeed, [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | m_pPLC->writeDWord(component, startAddress + 84, (int)(axisData.autoSpeed * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("\n写入成功: 自动速度, 地址: %d, 值: %lu\n", addr, value); |
| | | } |
| | |
| | | }); |
| | | |
| | | // 写入加速时间, 转换为毫秒 |
| | | m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 4, (int)(axisData.accelerationTime * 1000), [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | m_pPLC->writeDWord(component, startAddress + 62, (int)(axisData.accelerationTime * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("\n写入成功: 加速时间, 地址: %d, 值: %lu\n", addr, value); |
| | | } |
| | |
| | | }); |
| | | |
| | | // 写入减速时间, 转换为毫秒 |
| | | m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 6, (int)(axisData.decelerationTime * 1000), [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | m_pPLC->writeDWord(component, startAddress + 64, (int)(axisData.decelerationTime * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("\n写入成功: 减速时间, 地址: %d, 值: %lu\n", addr, value); |
| | | } |
| | |
| | | }); |
| | | |
| | | // 写入微动量 |
| | | m_pPLC->writeWord(MC::SOFT_COMPONENT::D, startAddress + 8, (int)axisData.jogDistance, [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | m_pPLC->writeWord(component, startAddress + 81, (int)(axisData.jogDistance * 1000.0), [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("\n写入成功: 微动量, 地址: %d, 值: %lu\n", addr, value); |
| | | } |
| | |
| | | }); |
| | | |
| | | // 写入定位点数据 |
| | | int positionStartAddress = startAddress + 10; |
| | | /* |
| | | int positionStartAddress = startAddress + 100; |
| | | for (size_t i = 0; i < axisData.positions.size(); ++i) { |
| | | const auto& position = axisData.positions[i]; |
| | | int positionAddress = positionStartAddress + (i * 2); |
| | | unsigned int positionAddress = positionStartAddress + (i * 4); |
| | | |
| | | m_pPLC->writeWord(MC::SOFT_COMPONENT::D, positionAddress, (int)position.second, [i](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | m_pPLC->writeWord(component, positionAddress, (int)position.second, [i](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("\n写入成功: 定位点 %d, 地址: %d, 值: %lu\n", i + 1, addr, value); |
| | | } |
| | |
| | | } |
| | | }); |
| | | } |
| | | */ |
| | | } |
| | | |
| | | void CAxisSettingsDlg::handleAxisOperation(AxisOperationType eOpType, bool bPressed) |
| | |
| | | RecipeManager& recipeManager = RecipeManager::getInstance(); |
| | | auto axisData = recipeManager.getAxis(nAxisId); |
| | | |
| | | std::string strCleanAddress = axisData.startAddress; |
| | | strCleanAddress.erase(std::remove_if(strCleanAddress.begin(), strCleanAddress.end(), |
| | | [](unsigned char c) { return !std::isdigit(c); }), strCleanAddress.end()); |
| | | if (strCleanAddress.empty()) { |
| | | int startAddress; |
| | | MC::SOFT_COMPONENT component; |
| | | if (!ParsePLCAddress(CString(axisData.startAddress.c_str()), component, startAddress)) { |
| | | AfxMessageBox(_T("无效的起始地址!")); |
| | | return; |
| | | } |
| | | |
| | | int nStartAddress = std::stoi(strCleanAddress); |
| | | |
| | | // 根据操作类型计算目标地址 |
| | | int nTargetAddress = nStartAddress; |
| | | int nTargetAddress = startAddress + 10; |
| | | switch (eOpType) { |
| | | case AxisOperationType::OPR: |
| | | nTargetAddress += 10; // OPR 信号地址 |
| | |
| | | } |
| | | |
| | | // 向 PLC 写入信号 |
| | | m_pPLC->writeBit(MC::SOFT_COMPONENT::D, nTargetAddress, bPressed, [eOpType, nTargetAddress, bPressed](IMcChannel* pChannel, int nAddr, DWORD nValue, int nFlag) { |
| | | m_pPLC->writeBit(component, nTargetAddress, bPressed, [eOpType, nTargetAddress, bPressed](IMcChannel* pChannel, int nAddr, DWORD nValue, int nFlag) { |
| | | if (nFlag == 0) { |
| | | TRACE("操作成功:类型=%d,地址=%d,值=%d\n", static_cast<int>(eOpType), nAddr, bPressed); |
| | | } |
| | |
| | | TRACE("操作失败:类型=%d,地址=%d,错误码=%d\n", static_cast<int>(eOpType), nAddr, nFlag); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | void CAxisSettingsDlg::readPLCDataToUI(int nAxisId) |
| | | { |
| | | CBLLabel* pLabels[] = { &m_staticFLS, &m_staticDOG, &m_staticRLS, &m_staticReady, &m_staticBusy, &m_staticErr }; |
| | | |
| | | // 从 RecipeManager 获取轴数据 |
| | | RecipeManager& recipeManager = RecipeManager::getInstance(); |
| | | auto axisData = recipeManager.getAxis(nAxisId); |
| | | |
| | | MC::SOFT_COMPONENT component; |
| | | int startAddress, endAddress, readSize; |
| | | if (!ParsePLCAddress(CString(axisData.startAddress.c_str()), component, startAddress)) { |
| | | AfxMessageBox(_T("无效的起始地址!")); |
| | | } |
| | | |
| | | // 从 OPR 信号地址开始读取 |
| | | startAddress += 10; |
| | | endAddress = startAddress + 24; |
| | | readSize = endAddress - startAddress + 1; |
| | | |
| | | // 回调处理输入数据 |
| | | auto funOnReadData = [this, startAddress, &pLabels](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) { |
| | | if (flag == 0) { |
| | | int nOffset = 0; |
| | | for (auto pLabel : pLabels) { |
| | | int value = CToolUnits::toInt16(&pData[(startAddress + nOffset) * 2]); |
| | | if (value == 0) { |
| | | SetStatusColor(*pLabel, FALSE); |
| | | } else { |
| | | SetStatusColor(*pLabel, TRUE); |
| | | } |
| | | |
| | | nOffset++; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | m_pPLC->readData(component, startAddress, readSize, funOnReadData); |
| | | } |
| | | |
| | | |
| | |
| | | if (TIMER_READ_PLC_DATA == nIDEvent) { |
| | | ASSERT(m_pPLC); |
| | | |
| | | int nAxisId = getCurrentSelectedAxisID(); |
| | | if (nAxisId == -1) { |
| | | return; |
| | | } |
| | | |
| | | int addr1, addr2, readSize; |
| | | addr1 = 5120; |
| | | addr2 = 5425; |
| | |
| | | SetDlgItemInt(IDC_EDIT_AXIS_CURR_ALARM_NUMBER, nAlarmCode); |
| | | } |
| | | }; |
| | | m_pPLC->readData(MC::SOFT_COMPONENT::D, addr1, readSize, funOnReadData); |
| | | //m_pPLC->readData(MC::SOFT_COMPONENT::D, addr1, readSize, funOnReadData); |
| | | |
| | | //readPLCDataToUI(nAxisId); |
| | | } |
| | | else if (nIDEvent == TIMER_JOG_ADD && m_bJogAddPressed) { |
| | | TRACE("持续发送 JOG+\n"); |
| | |
| | | void updateAxisSelection(int offset); |
| | | void updateDataFromUI(int nAxisId); |
| | | void switchToPage(int targetPage); |
| | | bool ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr); |
| | | void writeAxisDataToPLC(int nAxisId); |
| | | void handleAxisOperation(AxisOperationType eOpType, bool bPressed); |
| | | |
| | | void readPLCDataToUI(int nAxisId); |
| | | |
| | | private: |
| | | CPLC* m_pPLC; |
| | |
| | | |
| | | #define TIMER_INIT 1 |
| | | #define TIMER_READ_PLC_DATA 2 |
| | | #define TIMER_READ_UPDATE 3 |
| | | |
| | | #define LABEL_ID_BEGIN 2000 |
| | | #define ADDR_WND _T("ADDR_WND") |
| | | |
| | | #define ID_MSG_IO_CLICK WM_USER + 101 |
| | | |
| | | // CIOMonitoringDlg 对话框 |
| | | |
| | |
| | | int colWidthLarge = availableWidth * 4 / 14; // 大宽度列比例 |
| | | int groupWidth = colWidthSmall * 2 + colWidthLarge; // 每组总宽度 |
| | | |
| | | UINT id = LABEL_ID_BEGIN; |
| | | for (int i = 0; i < m_nRowsPerPage; ++i) { |
| | | // 每一行的起始 Y 坐标 |
| | | int y = topMargin + i * (rowHeight + verticalSpacing); |
| | | |
| | | // 创建第 1 组 (0, 1, 2) |
| | | int x = sideMargin; // 从左边距开始 |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter); |
| | | CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter); |
| | | CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false); |
| | | CreateStaticControl(++id, x, y, colWidthLarge, rowHeight, _T("描述文本"), false); |
| | | |
| | | // 第 2 组起始位置,加上组间距 |
| | | x += colWidthLarge + groupSpacing; |
| | | |
| | | // 创建第 2 组 (3, 4, 5) |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter, [this, i]() { |
| | | id++; |
| | | CWnd* pBtn = CreateStaticControl(id, x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter, [this, id, i, x, y]() { |
| | | // 自定义点击事件的逻辑 |
| | | auto* pControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 3]); |
| | | CString currentText; |
| | | pControl->GetWindowText(currentText); |
| | | |
| | | BOOL bOn = FALSE; |
| | | if (currentText == _T("OFF")) { |
| | | //pControl->SetBkColor(RGB(0, 255, 0)); // 绿色背景 |
| | | //pControl->SetText(_T("ON")); // 更新文本为 ON |
| | | bOn = TRUE; |
| | | } |
| | | else { |
| | | //pControl->SetBkColor(RGB(255, 0, 0)); // 红色背景 |
| | | //pControl->SetText(_T("OFF")); // 更新文本为 OFF |
| | | bOn = FALSE; |
| | | } |
| | | |
| | | pControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 4]); |
| | | pControl->GetWindowText(currentText); |
| | | |
| | | int nAddress; |
| | | MC::SOFT_COMPONENT component; |
| | | if (ParsePLCAddress(currentText, component, nAddress) && m_pPLC) { |
| | | TRACE("地址解析成功: %s\n", currentText); |
| | | m_pPLC->writeBit(component, nAddress, bOn, [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("写入成功: 地址: %d, 值: %lu\n", addr, value); |
| | | } |
| | | else { |
| | | TRACE("写入失败: 地址: %d, 错误码: %d\n", addr, flag); |
| | | } |
| | | }); |
| | | } |
| | | PostMessage(ID_MSG_IO_CLICK, id, currentText == _T("OFF") ? 1 : 0); |
| | | }); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter); |
| | | CWnd* pLabel = CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false); |
| | | CreateStaticControl(++id, x, y, colWidthLarge, rowHeight, _T("描述文本"), false); |
| | | ::SetProp(pBtn->GetSafeHwnd(), ADDR_WND, pLabel); |
| | | } |
| | | } |
| | | |
| | | void CIOMonitoringDlg::CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder, TextAlign alignment, std::function<void()> clickCallback) |
| | | CWnd* CIOMonitoringDlg::CreateStaticControl(UINT id, int x, int y, int width, int height, const CString& text, bool hasBorder, TextAlign alignment, std::function<void()> clickCallback) |
| | | { |
| | | // 创建动态控件 |
| | | CBLLabel* pStatic = new CBLLabel(); |
| | |
| | | if (hasBorder) { |
| | | style |= WS_BORDER; // 添加边框 |
| | | } |
| | | pStatic->Create(text, style, CRect(x, y, x + width, y + height), this); |
| | | pStatic->Create(text, style, CRect(x, y, x + width, y + height), this, id); |
| | | |
| | | // 设置文本对齐方式 |
| | | pStatic->SetAlignment(alignment); |
| | |
| | | |
| | | // 存储控件指针 |
| | | m_staticControls.push_back(pStatic); |
| | | |
| | | return pStatic; |
| | | } |
| | | |
| | | CWnd* CIOMonitoringDlg::GetStaticControl(UINT id) |
| | | { |
| | | for (auto item : m_staticControls) { |
| | | TRACE(">>id:%d\n", item->GetDlgCtrlID()); |
| | | if (item->GetDlgCtrlID() == id) { |
| | | return item; |
| | | } |
| | | } |
| | | |
| | | return nullptr; |
| | | } |
| | | |
| | | CString& CIOMonitoringDlg::GetStaticControlAddrText(UINT id, CString& strAddr) |
| | | { |
| | | CWnd* pBtn = GetStaticControl(id); |
| | | if (pBtn != nullptr) { |
| | | CWnd* pLabel = (CWnd*)::GetProp(pBtn->GetSafeHwnd(), ADDR_WND); |
| | | if (pLabel != nullptr) { |
| | | pLabel->GetWindowText(strAddr); |
| | | } |
| | | } |
| | | |
| | | return strAddr; |
| | | } |
| | | |
| | | void CIOMonitoringDlg::DisplayCurrentPage() |
| | |
| | | |
| | | // 提取组件类型(第一个字符) |
| | | TCHAR componentChar = address[0]; |
| | | if (address.Left(2) == _T("ZR")) { |
| | | component = MC::SOFT_COMPONENT::ZR; |
| | | // 提取数字部分(去除ZR前缀) |
| | | CString numericAddress = address.Mid(2); |
| | | addr = _tcstoul(numericAddress, nullptr, 16); |
| | | return addr != 0 || numericAddress.CompareNoCase(_T("0")) == 0; // 如果是 "0",也认为有效 |
| | | } |
| | | |
| | | // 对于其他组件,按照常规规则处理 |
| | | CString hexAddress = address.Mid(1); |
| | | switch (componentChar) { |
| | | case 'D': |
| | | component = MC::SOFT_COMPONENT::D; |
| | | addr = _ttoi(hexAddress); |
| | | break; |
| | | case 'M': |
| | | component = MC::SOFT_COMPONENT::M; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'X': |
| | | component = MC::SOFT_COMPONENT::X; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'Y': |
| | | component = MC::SOFT_COMPONENT::Y; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'W': |
| | | component = MC::SOFT_COMPONENT::W; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'L': |
| | | component = MC::SOFT_COMPONENT::L; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'S': |
| | | component = MC::SOFT_COMPONENT::S; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'B': |
| | | component = MC::SOFT_COMPONENT::B; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | case 'F': |
| | | component = MC::SOFT_COMPONENT::F; |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | break; |
| | | default: |
| | | return false; |
| | | } |
| | | |
| | | CString hexAddress = address.Mid(1); |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | // 检查地址是否有效 |
| | | if (addr == 0 && hexAddress.CompareNoCase(_T("0")) != 0) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void CIOMonitoringDlg::UpdatePLCStates() |
| | | bool CIOMonitoringDlg::GeneratePLCAddress(MC::SOFT_COMPONENT component, int addr, CString& address, bool bHexFormat) |
| | | { |
| | | // 示例:从 PLC 获取值,这里用随机值模拟 |
| | | //for (size_t i = 0; i < m_inputPLCAddresses.size(); ++i) { |
| | | // // 模拟获取输入状态 |
| | | // bool inputState = (rand() % 2 == 0); // 偶尔为 true/false |
| | | // auto* inputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 0]); |
| | | // inputControl->SetBkColor(inputState ? RGB(0, 255, 0) : RGB(255, 0, 0)); |
| | | // inputControl->SetText(inputState ? _T("ON") : _T("OFF")); |
| | | //} |
| | | |
| | | //for (size_t i = 0; i < m_outputPLCAddresses.size(); ++i) { |
| | | // // 模拟获取输出状态 |
| | | // bool outputState = (rand() % 2 == 0); // 偶尔为 true/false |
| | | // auto* outputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 3]); |
| | | // outputControl->SetBkColor(outputState ? RGB(0, 255, 0) : RGB(255, 0, 0)); |
| | | // outputControl->SetText(outputState ? _T("ON") : _T("OFF")); |
| | | //} |
| | | |
| | | // 输入地址的读取 |
| | | if (!m_inputPLCAddresses.empty()) { |
| | | // 获取起始地址和长度 |
| | | CString startAddressStr = m_inputPLCAddresses.front(); |
| | | CString endAddressStr = m_inputPLCAddresses.back(); |
| | | MC::SOFT_COMPONENT component; |
| | | int startAddress, endAddress; |
| | | |
| | | // 解析起始和结束地址 |
| | | if (ParsePLCAddress(startAddressStr, component, startAddress) && |
| | | ParsePLCAddress(endAddressStr, component, endAddress)) { |
| | | int inputSize = endAddress - startAddress + 1; |
| | | |
| | | // 回调处理输入数据 |
| | | auto funOnReadInput = [this, startAddress](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) { |
| | | if (nDataSize == (unsigned int)(m_inputPLCAddresses.size()) && flag == 0) { |
| | | for (size_t i = 0; i < m_inputPLCAddresses.size(); ++i) { |
| | | int offset = i; |
| | | int value = CToolUnits::toInt16(&pData[offset]); |
| | | |
| | | auto* inputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 0]); // 第 0 列 |
| | | inputControl->SetBkColor(value ? RGB(0, 255, 0) : RGB(255, 0, 0)); // 更新背景颜色 |
| | | inputControl->SetText(value ? _T("ON") : _T("OFF")); // 更新文本 |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 读取输入数据 |
| | | m_pPLC->readData(component, startAddress, inputSize, funOnReadInput); |
| | | } |
| | | // 根据组件类型生成前缀 |
| | | CString prefix; |
| | | switch (component) { |
| | | case MC::SOFT_COMPONENT::D: |
| | | prefix = _T("D"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::M: |
| | | prefix = _T("M"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::X: |
| | | prefix = _T("X"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::Y: |
| | | prefix = _T("Y"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::W: |
| | | prefix = _T("W"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::L: |
| | | prefix = _T("L"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::S: |
| | | prefix = _T("S"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::B: |
| | | prefix = _T("B"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::F: |
| | | prefix = _T("F"); |
| | | break; |
| | | case MC::SOFT_COMPONENT::ZR: |
| | | prefix = _T("ZR"); |
| | | break; |
| | | default: |
| | | return false; // 如果是未知组件类型,返回失败 |
| | | } |
| | | |
| | | // 输出地址的读取 |
| | | if (!m_outputPLCAddresses.empty()) { |
| | | // 获取起始地址和长度 |
| | | CString startAddressStr = m_outputPLCAddresses.front(); |
| | | CString endAddressStr = m_outputPLCAddresses.back(); |
| | | MC::SOFT_COMPONENT component; |
| | | int startAddress, endAddress; |
| | | // 根据 bHexFormat 判断输出格式 |
| | | CString strAddr; |
| | | if (bHexFormat) { |
| | | strAddr.Format(_T("%X"), addr); // 十六进制格式 |
| | | } |
| | | else { |
| | | strAddr.Format(_T("%d"), addr); // 十进制格式 |
| | | } |
| | | |
| | | // 解析起始和结束地址 |
| | | if (ParsePLCAddress(startAddressStr, component, startAddress) && |
| | | ParsePLCAddress(endAddressStr, component, endAddress)) { |
| | | int outputSize = endAddress - startAddress + 1; |
| | | // 生成最终的地址字符串 |
| | | address = prefix + strAddr; |
| | | return true; |
| | | } |
| | | |
| | | // 回调处理输出数据 |
| | | auto funOnReadOutput = [this, startAddress](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) { |
| | | if (nDataSize == (unsigned int)(m_outputPLCAddresses.size()) && flag == 0) { |
| | | for (size_t i = 0; i < m_outputPLCAddresses.size(); ++i) { |
| | | int offset = i; |
| | | int value = CToolUnits::toInt16(&pData[offset]); |
| | | // 处理PLC数据读取的通用方法 |
| | | void CIOMonitoringDlg::ReadPLCData(MC::SOFT_COMPONENT softComponent, int startAddr, int endAddr, std::function<void(IMcChannel*, int, char*, unsigned int, int)> callback) |
| | | { |
| | | int nSize; |
| | | // 检查地址是否有效,以及组件是否匹配 |
| | | nSize = endAddr - startAddr + 1; |
| | | if (nSize < 1) { |
| | | return; |
| | | } |
| | | |
| | | auto* outputControl = static_cast<CBLLabel*>(m_staticControls[i * m_nCols + 3]); // 第 3 列 |
| | | outputControl->SetBkColor(value ? RGB(0, 255, 0) : RGB(255, 0, 0)); // 更新背景颜色 |
| | | outputControl->SetText(value ? _T("ON") : _T("OFF")); // 更新文本 |
| | | } |
| | | // 读取数据 |
| | | m_pPLC->readData(softComponent, startAddr, nSize, callback); |
| | | } |
| | | |
| | | void CIOMonitoringDlg::ReadPLCStates() |
| | | { |
| | | if (m_displayData.empty()) { |
| | | return; |
| | | } |
| | | |
| | | auto startData = m_displayData.front(); |
| | | auto endData = m_displayData.back(); |
| | | MC::SOFT_COMPONENT startSoftComponent, endSoftComponent; |
| | | int startAddr, endAddr; |
| | | ParsePLCAddress(CString(startData.inputAddress.c_str()), startSoftComponent, startAddr); |
| | | ParsePLCAddress(CString(endData.inputAddress.c_str()), endSoftComponent, endAddr); |
| | | ASSERT(startSoftComponent == endSoftComponent); |
| | | |
| | | // 读取输入数据 |
| | | ReadPLCData(startSoftComponent, startAddr, endAddr, [this, startAddr](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) { |
| | | for (auto& data : m_displayData) { |
| | | int nAddress; |
| | | MC::SOFT_COMPONENT enComponent; |
| | | if (ParsePLCAddress(CString(data.inputAddress.c_str()), enComponent, nAddress)) { |
| | | int nOffset = nAddress - startAddr; |
| | | if (nOffset >= 0 && nOffset < nDataSize) { |
| | | int byteIndex = nOffset / 8; |
| | | int bitIndex = nOffset % 8; |
| | | data.bInputStates = CToolUnits::getBit(pData[byteIndex], bitIndex); |
| | | } |
| | | }; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | // 读取输出数据 |
| | | m_pPLC->readData(component, startAddress, outputSize, funOnReadOutput); |
| | | // 读取输出数据 |
| | | int startAddr2, endAddr2; |
| | | ParsePLCAddress(CString(startData.outputAddress.c_str()), startSoftComponent, startAddr2); |
| | | ParsePLCAddress(CString(endData.outputAddress.c_str()), endSoftComponent, endAddr2); |
| | | ASSERT(startSoftComponent == endSoftComponent); |
| | | ReadPLCData(startSoftComponent, startAddr2, endAddr2, [this, startAddr2](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) { |
| | | for (auto& data : m_displayData) { |
| | | int nAddress; |
| | | MC::SOFT_COMPONENT enComponent; |
| | | if (ParsePLCAddress(CString(data.outputAddress.c_str()), enComponent, nAddress)) { |
| | | int nOffset = nAddress - startAddr2; |
| | | if (nOffset >= 0 && nOffset < nDataSize) { |
| | | int byteIndex = nOffset / 8; |
| | | int bitIndex = nOffset % 8; |
| | | data.bOutputStates = CToolUnits::getBit(pData[byteIndex], bitIndex); |
| | | TRACE(">>>> %d %d %d |||| %d\n", nOffset, byteIndex, bitIndex, data.bOutputStates); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | void CIOMonitoringDlg::UpdatePLCStatesToUI() |
| | | { |
| | | int startIndex = (m_nCurrentPage - 1) * m_nRowsPerPage; |
| | | int endIndex = min(startIndex + m_nRowsPerPage, static_cast<int>(m_displayData.size())); |
| | | |
| | | for (int i = 0; i < m_nRowsPerPage; ++i) { |
| | | int row = i; |
| | | |
| | | if (startIndex + i < endIndex) { |
| | | const auto& data = m_displayData[startIndex + i]; |
| | | // 设置内容和背景颜色 |
| | | m_staticControls[row * m_nCols + 0]->SetText(data.bInputStates ? _T("ON") : _T("OFF")); |
| | | m_staticControls[row * m_nCols + 0]->SetBkColor(data.bInputStates ? RGB(0, 255, 0) : RGB(255, 0, 0)); |
| | | |
| | | m_staticControls[row * m_nCols + 3]->SetText(data.bOutputStates ? _T("ON") : _T("OFF")); |
| | | m_staticControls[row * m_nCols + 3]->SetBkColor(data.bOutputStates ? RGB(0, 255, 0) : RGB(255, 0, 0)); |
| | | } |
| | | } |
| | | } |
| | |
| | | BEGIN_MESSAGE_MAP(CIOMonitoringDlg, CDialogEx) |
| | | ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CIOMonitoringDlg::OnBnClickedButtonPrevPage) |
| | | ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CIOMonitoringDlg::OnBnClickedButtonNextPage) |
| | | ON_MESSAGE(ID_MSG_IO_CLICK, &CIOMonitoringDlg::OnIoClicked) |
| | | ON_WM_SIZE() |
| | | ON_WM_TIMER() |
| | | ON_WM_CLOSE() |
| | |
| | | |
| | | CreateDynamicControls(); |
| | | DisplayCurrentPage(); |
| | | GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurrentPage > 1); |
| | | GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurrentPage < m_nTotalPages); |
| | | |
| | | SetTimer(TIMER_READ_PLC_DATA, 500, nullptr); |
| | | SetTimer(TIMER_READ_UPDATE, 200, nullptr); |
| | | |
| | | return TRUE; // return TRUE unless you set the focus to a control |
| | | // 异常: OCX 属性页应返回 FALSE |
| | |
| | | else { |
| | | AfxMessageBox(_T("已经是第一页!")); |
| | | } |
| | | |
| | | GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurrentPage > 1); |
| | | GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurrentPage < m_nTotalPages); |
| | | } |
| | | |
| | | void CIOMonitoringDlg::OnBnClickedButtonNextPage() |
| | |
| | | else { |
| | | AfxMessageBox(_T("已经是最后一页!")); |
| | | } |
| | | } |
| | | |
| | | GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurrentPage > 1); |
| | | GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurrentPage < m_nTotalPages); |
| | | LRESULT CIOMonitoringDlg::OnIoClicked(WPARAM wParam, LPARAM lParam) |
| | | { |
| | | CString strAddr; |
| | | GetStaticControlAddrText((UINT)wParam, strAddr); |
| | | |
| | | BOOL bOn = (BOOL)lParam; |
| | | int nAddress; |
| | | MC::SOFT_COMPONENT component; |
| | | if (ParsePLCAddress(strAddr, component, nAddress) && m_pPLC) { |
| | | TRACE("地址解析成功: %s %d\n", strAddr, GetCurrentThreadId()); |
| | | int n = m_pPLC->writeBit(component, nAddress, bOn, [](IMcChannel* pChannel, int addr, DWORD value, int flag) { |
| | | if (flag == 0) { |
| | | TRACE("写入成功: 地址: %d, 值: %lu\n", addr, value); |
| | | } |
| | | else { |
| | | TRACE("写入失败: 地址: %d, 错误码: %d\n", addr, flag); |
| | | } |
| | | }); |
| | | TRACE("地址解析成功2: %d\n", n); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void CIOMonitoringDlg::OnTimer(UINT_PTR nIDEvent) |
| | |
| | | // TODO: 在此添加消息处理程序代码和/或调用默认值 |
| | | if (TIMER_READ_PLC_DATA == nIDEvent) { |
| | | ASSERT(m_pPLC); |
| | | UpdatePLCStates(); |
| | | Sleep(100); |
| | | ReadPLCStates(); |
| | | } |
| | | else if (TIMER_READ_UPDATE == nIDEvent) { |
| | | ASSERT(m_pPLC); |
| | | UpdatePLCStatesToUI(); |
| | | } |
| | | |
| | | CDialogEx::OnTimer(nIDEvent); |
| | | } |
| | | |
| | |
| | | { |
| | | // TODO: 在此添加消息处理程序代码和/或调用默认值 |
| | | KillTimer(TIMER_READ_PLC_DATA); |
| | | KillTimer(TIMER_READ_UPDATE); |
| | | CDialogEx::OnClose(); |
| | | } |
| | |
| | | void AdjustControls(float dScaleX, float dScaleY); // 调整控件大小 |
| | | void AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight); // 调整控件字体大小 |
| | | |
| | | void UpdatePageInfo(); // 更新分页信息 |
| | | void CreateDynamicControls(); // 动态创建控件 |
| | | void CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder = false, TextAlign alignment = AlignLeft, std::function<void()> clickCallback = nullptr); // 创建静态控件 |
| | | void DisplayCurrentPage(); // 显示当前页数据 |
| | | void ClearDynamicControls(); // 清除动态创建的控件 |
| | | void UpdatePageInfo(); // 更新分页信息 |
| | | void CreateDynamicControls(); // 动态创建控件 |
| | | CWnd* CreateStaticControl(UINT id, int x, int y, int width, int height, const CString& text, bool hasBorder = false, TextAlign alignment = AlignLeft, std::function<void()> clickCallback = nullptr); // 创建静态控件 |
| | | CWnd* GetStaticControl(UINT id); // 获取静态控件 |
| | | CString& GetStaticControlAddrText(UINT id, CString& strAddr); // 获取静态控件地址文本 |
| | | void DisplayCurrentPage(); // 显示当前页数据 |
| | | void ClearDynamicControls(); // 清除动态创建的控件 |
| | | bool ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr); // 解析 PLC 地址 |
| | | void UpdatePLCStates(); // 定时器更新状态的方法 |
| | | bool GeneratePLCAddress(MC::SOFT_COMPONENT component, int addr, CString& address, bool bHexFormat = false); // 生成 PLC 地址 |
| | | void ReadPLCData(MC::SOFT_COMPONENT softComponent, int startAddr, int endAddr, std::function<void(IMcChannel*, int, char*, unsigned int, int)> callback); // 读取 PLC 数据 |
| | | void ReadPLCStates(); // 定时器读PLC |
| | | void UpdatePLCStatesToUI(); // 定时器更新状态的方法 |
| | | |
| | | private: |
| | | CPLC* m_pPLC; |
| | |
| | | afx_msg void OnSize(UINT nType, int cx, int cy); |
| | | afx_msg void OnBnClickedButtonPrevPage(); |
| | | afx_msg void OnBnClickedButtonNextPage(); |
| | | afx_msg LRESULT OnIoClicked(WPARAM wParam, LPARAM lParam); |
| | | afx_msg void OnTimer(UINT_PTR nIDEvent); |
| | | afx_msg void OnClose(); |
| | | }; |