| | |
| | | #include "BondEq.h" |
| | | #include "afxdialogex.h" |
| | | #include "IOMonitoringDlg.h" |
| | | #include "ToolUnits.h" |
| | | |
| | | #define TIMER_INIT 1 |
| | | #define TIMER_READ_PLC_DATA 2 |
| | | |
| | | // CIOMonitoringDlg 对话框 |
| | | |
| | |
| | | m_nTotalPages = (m_displayData.size() + m_nRowsPerPage - 1) / m_nRowsPerPage; |
| | | } |
| | | |
| | | void CIOMonitoringDlg::SetPLC(CPLC* pPLC) |
| | | { |
| | | ASSERT(pPLC); |
| | | m_pPLC = pPLC; |
| | | } |
| | | |
| | | CFont* CIOMonitoringDlg::GetOrCreateFont(int nFontSize) |
| | | { |
| | | auto it = m_mapFonts.find(nFontSize); |
| | |
| | | CWnd* pWnd = GetWindow(GW_CHILD); |
| | | while (pWnd) { |
| | | int nCtrlID = pWnd->GetDlgCtrlID(); |
| | | if (nCtrlID != -1 && m_mapCtrlLayouts.find(nCtrlID) != m_mapCtrlLayouts.end()) |
| | | { |
| | | if (nCtrlID != -1 && m_mapCtrlLayouts.find(nCtrlID) != m_mapCtrlLayouts.end()) { |
| | | CRect originalRect = m_mapCtrlLayouts[nCtrlID]; |
| | | CRect newRect( |
| | | static_cast<int>(originalRect.left * dScaleX), |
| | |
| | | // 根据控件高度动态调整字体大小 |
| | | int fontSize = nHeight / 2; |
| | | if (fontSize < 8) fontSize = 8; |
| | | if (fontSize < 24) fontSize = 24; |
| | | |
| | | // 获取或创建字体 |
| | | CFont* pFont = GetOrCreateFont(fontSize); |
| | |
| | | int colWidthLarge = availableWidth * 4 / 14; // 大宽度列比例 |
| | | int groupWidth = colWidthSmall * 2 + colWidthLarge; // 每组总宽度 |
| | | |
| | | for (int i = 0; i < m_nRowsPerPage; ++i) |
| | | { |
| | | 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); |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("X1000")); |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false, true); |
| | | CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false); |
| | | |
| | | // 第 2 组起始位置,加上组间距 |
| | | x += colWidthLarge + groupSpacing; |
| | | |
| | | // 创建第 2 组 (3, 4, 5) |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true); |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter, [this, i]() { |
| | | // 自定义点击事件的逻辑 |
| | | 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); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("Y1010")); |
| | | CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter); |
| | | x += colWidthSmall; |
| | | CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false, true); |
| | | CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false); |
| | | } |
| | | } |
| | | |
| | | void CIOMonitoringDlg::CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder, bool alignLeft) |
| | | void CIOMonitoringDlg::CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder, TextAlign alignment, std::function<void()> clickCallback) |
| | | { |
| | | // 创建动态控件 |
| | | CBLLabel* pStatic = new CBLLabel(); |
| | | DWORD style = WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE; // 确保垂直居中 |
| | | if (hasBorder) { |
| | | style |= WS_BORDER; // 添加边框 |
| | | style |= WS_BORDER; // 添加边框 |
| | | } |
| | | if (alignLeft) { |
| | | style |= SS_LEFT; // 左对齐文本 |
| | | } |
| | | else { |
| | | style |= SS_CENTER; // 居中文本 |
| | | } |
| | | |
| | | CStatic* pStatic = new CStatic(); |
| | | pStatic->Create(text, style, CRect(x, y, x + width, y + height), this); |
| | | |
| | | // 动态设置字体大小 |
| | | CFont* pFont = GetOrCreateFont(height / 3); |
| | | // 设置文本对齐方式 |
| | | pStatic->SetAlignment(alignment); |
| | | |
| | | // 设置动态字体调整,并设置字体大小(动态字体会根据控件大小调整) |
| | | int nSize = height / 3; |
| | | CFont* pFont = GetOrCreateFont(nSize); |
| | | pStatic->SetFont(pFont); |
| | | pStatic->SetFontSize(nSize); |
| | | pStatic->SetDynamicFont(TRUE); |
| | | |
| | | // 设置回调 |
| | | if (clickCallback) { |
| | | pStatic->SetClickCallback(clickCallback); |
| | | } |
| | | |
| | | // 存储控件指针 |
| | | m_staticControls.push_back(pStatic); |
| | |
| | | int startIndex = (m_nCurrentPage - 1) * m_nRowsPerPage; |
| | | int endIndex = min(startIndex + m_nRowsPerPage, static_cast<int>(m_displayData.size())); |
| | | |
| | | for (int i = startIndex; i < endIndex; ++i) |
| | | { |
| | | const auto& data = m_displayData[i]; |
| | | int row = i - startIndex; |
| | | m_inputPLCAddresses.clear(); |
| | | m_outputPLCAddresses.clear(); |
| | | |
| | | m_staticControls[row * m_nCols + 0]->SetWindowText(_T("OFF")); |
| | | m_staticControls[row * m_nCols + 1]->SetWindowText(CString(data.inputAddress.c_str())); |
| | | m_staticControls[row * m_nCols + 2]->SetWindowText(CString(data.inputDescription.c_str())); |
| | | m_staticControls[row * m_nCols + 3]->SetWindowText(_T("OFF")); |
| | | m_staticControls[row * m_nCols + 4]->SetWindowText(CString(data.outputAddress.c_str())); |
| | | m_staticControls[row * m_nCols + 5]->SetWindowText(CString(data.outputDescription.c_str())); |
| | | for (int i = 0; i < m_nRowsPerPage; ++i) { |
| | | int row = i; |
| | | |
| | | if (startIndex + i < endIndex) { |
| | | const auto& data = m_displayData[startIndex + i]; |
| | | |
| | | // 添加 PLC 地址到容器中 |
| | | m_inputPLCAddresses.push_back(CString(data.inputAddress.c_str())); // 1 列 |
| | | m_outputPLCAddresses.push_back(CString(data.outputAddress.c_str())); // 4 列 |
| | | |
| | | // 显示控件并设置内容 |
| | | m_staticControls[row * m_nCols + 0]->SetWindowText(_T("OFF")); |
| | | m_staticControls[row * m_nCols + 0]->ShowWindow(SW_SHOW); |
| | | m_staticControls[row * m_nCols + 0]->SetBkColor(RGB(255, 0, 0)); |
| | | |
| | | m_staticControls[row * m_nCols + 1]->SetWindowText(CString(data.inputAddress.c_str())); |
| | | m_staticControls[row * m_nCols + 1]->ShowWindow(SW_SHOW); |
| | | |
| | | m_staticControls[row * m_nCols + 2]->SetWindowText(CString(data.inputDescription.c_str())); |
| | | m_staticControls[row * m_nCols + 2]->ShowWindow(SW_SHOW); |
| | | |
| | | m_staticControls[row * m_nCols + 3]->SetWindowText(_T("OFF")); |
| | | m_staticControls[row * m_nCols + 3]->ShowWindow(SW_SHOW); |
| | | m_staticControls[row * m_nCols + 3]->SetBkColor(RGB(255, 0, 0)); |
| | | |
| | | m_staticControls[row * m_nCols + 4]->SetWindowText(CString(data.outputAddress.c_str())); |
| | | m_staticControls[row * m_nCols + 4]->ShowWindow(SW_SHOW); |
| | | |
| | | m_staticControls[row * m_nCols + 5]->SetWindowText(CString(data.outputDescription.c_str())); |
| | | m_staticControls[row * m_nCols + 5]->ShowWindow(SW_SHOW); |
| | | } |
| | | else { |
| | | // 隐藏这一行的所有控件 |
| | | for (int col = 0; col < m_nCols; ++col) { |
| | | m_staticControls[row * m_nCols + col]->ShowWindow(SW_HIDE); |
| | | } |
| | | } |
| | | } |
| | | |
| | | UpdatePageInfo(); |
| | | } |
| | | |
| | | void CIOMonitoringDlg::ClearDynamicControls() |
| | | { |
| | | for (auto* pStatic : m_staticControls) |
| | | { |
| | | if (pStatic) |
| | | { |
| | | for (auto* pStatic : m_staticControls) { |
| | | if (pStatic) { |
| | | pStatic->DestroyWindow(); |
| | | delete pStatic; |
| | | } |
| | |
| | | m_staticControls.clear(); |
| | | } |
| | | |
| | | bool CIOMonitoringDlg::ParsePLCAddress(const CString& address, MC::SOFT_COMPONENT& component, int& addr) |
| | | { |
| | | if (address.GetLength() < 2) { |
| | | return false; |
| | | } |
| | | |
| | | // 提取组件类型(第一个字符) |
| | | TCHAR componentChar = address[0]; |
| | | switch (componentChar) { |
| | | case 'D': |
| | | component = MC::SOFT_COMPONENT::D; |
| | | break; |
| | | case 'M': |
| | | component = MC::SOFT_COMPONENT::M; |
| | | break; |
| | | case 'X': |
| | | component = MC::SOFT_COMPONENT::X; |
| | | break; |
| | | case 'Y': |
| | | component = MC::SOFT_COMPONENT::Y; |
| | | break; |
| | | default: |
| | | return false; |
| | | } |
| | | |
| | | CString hexAddress = address.Mid(1); |
| | | addr = _tcstoul(hexAddress, nullptr, 16); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void CIOMonitoringDlg::UpdatePLCStates() |
| | | { |
| | | // 示例:从 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); |
| | | } |
| | | } |
| | | |
| | | // 输出地址的读取 |
| | | if (!m_outputPLCAddresses.empty()) { |
| | | // 获取起始地址和长度 |
| | | CString startAddressStr = m_outputPLCAddresses.front(); |
| | | CString endAddressStr = m_outputPLCAddresses.back(); |
| | | MC::SOFT_COMPONENT component; |
| | | int startAddress, endAddress; |
| | | |
| | | // 解析起始和结束地址 |
| | | if (ParsePLCAddress(startAddressStr, component, startAddress) && |
| | | ParsePLCAddress(endAddressStr, component, endAddress)) { |
| | | int outputSize = endAddress - startAddress + 1; |
| | | |
| | | // 回调处理输出数据 |
| | | 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]); |
| | | |
| | | 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(component, startAddress, outputSize, funOnReadOutput); |
| | | } |
| | | } |
| | | } |
| | | |
| | | BEGIN_MESSAGE_MAP(CIOMonitoringDlg, CDialogEx) |
| | | ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CIOMonitoringDlg::OnBnClickedButtonPrevPage) |
| | | ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CIOMonitoringDlg::OnBnClickedButtonNextPage) |
| | | ON_WM_SIZE() |
| | | ON_WM_TIMER() |
| | | ON_WM_CLOSE() |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | CreateDynamicControls(); |
| | | DisplayCurrentPage(); |
| | | |
| | | SetTimer(TIMER_READ_PLC_DATA, 500, nullptr); |
| | | |
| | | return TRUE; // return TRUE unless you set the focus to a control |
| | | // 异常: OCX 属性页应返回 FALSE |
| | | } |
| | |
| | | AfxMessageBox(_T("已经是最后一页!")); |
| | | } |
| | | } |
| | | |
| | | void CIOMonitoringDlg::OnTimer(UINT_PTR nIDEvent) |
| | | { |
| | | // TODO: 在此添加消息处理程序代码和/或调用默认值 |
| | | if (TIMER_READ_PLC_DATA == nIDEvent) { |
| | | ASSERT(m_pPLC); |
| | | UpdatePLCStates(); |
| | | Sleep(100); |
| | | } |
| | | CDialogEx::OnTimer(nIDEvent); |
| | | } |
| | | |
| | | void CIOMonitoringDlg::OnClose() |
| | | { |
| | | // TODO: 在此添加消息处理程序代码和/或调用默认值 |
| | | KillTimer(TIMER_READ_PLC_DATA); |
| | | CDialogEx::OnClose(); |
| | | } |