LAPTOP-SNT8I5JK\Boounion
2024-12-12 016703bb359382dc1de4ac204da47b6f29c55c81
SourceCode/Bond/BondEq/View/IOMonitoringDlg.cpp
@@ -5,7 +5,16 @@
#include "BondEq.h"
#include "afxdialogex.h"
#include "IOMonitoringDlg.h"
#include "ToolUnits.h"
#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 对话框
@@ -16,7 +25,7 @@
{
   m_nCurrentPage = 1;
   m_nTotalPages = 1;
   m_nRowsPerPage = 10;
   m_nRowsPerPage = 16;
   m_nCols = 6;
}
@@ -50,7 +59,13 @@
   // 计算页数
   m_nCurrentPage = 1;
   m_nTotalPages = (m_displayData.size() + m_nRowsPerPage - 1) / m_nRowsPerPage;
   m_nTotalPages = ((int)m_displayData.size() + m_nRowsPerPage - 1) / m_nRowsPerPage;
}
void CIOMonitoringDlg::SetPLC(CPLC* pPLC)
{
   ASSERT(pPLC);
   m_pPLC = pPLC;
}
CFont* CIOMonitoringDlg::GetOrCreateFont(int nFontSize)
@@ -88,8 +103,7 @@
   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),
@@ -147,7 +161,7 @@
   ScreenToClient(&nextButtonRect);
   int buttonHeight = prevButtonRect.Height();     // 按钮的高度
   int topMargin = rect.Height() * 0.05;           // 顶部保留 5% 的高度
   int topMargin = int(rect.Height() * 0.05);      // 顶部保留 5% 的高度
   int bottomMargin = buttonHeight + topMargin;    // 底部保留按钮高度加间距
   int sideMargin = topMargin;                     // 左右间距与顶部间距相同
   int groupSpacing = 20;                          // 两组之间的间距
@@ -162,53 +176,97 @@
   int colWidthLarge = availableWidth * 4 / 14;      // 大宽度列比例
   int groupWidth = colWidthSmall * 2 + colWidthLarge; // 每组总宽度
   for (int i = 0; i < m_nRowsPerPage; ++i)
   {
   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);
      CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter);
      x += colWidthSmall;
      CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("X1000"));
      CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter);
      x += colWidthSmall;
      CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false, true);
      CreateStaticControl(++id, x, y, colWidthLarge, rowHeight, _T("描述文本"), false);
      // 第 2 组起始位置,加上组间距
      x += colWidthLarge + groupSpacing;
      // 创建第 2 组 (3, 4, 5)
      CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true);
      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);
         PostMessage(ID_MSG_IO_CLICK, id, currentText == _T("OFF") ? 1 : 0);
      });
      x += colWidthSmall;
      CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("Y1010"));
      CWnd* pLabel = CreateStaticControl(++id, x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter);
      x += colWidthSmall;
      CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false, true);
      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, bool alignLeft)
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();
   DWORD style = WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE; // 确保垂直居中
   if (hasBorder) {
      style |= WS_BORDER;      // 添加边框
   if (!hasBorder) {
      pStatic->DisableBorder();
   }
   if (alignLeft) {
      style |= SS_LEFT;      // 左对齐文本
   }
   else {
      style |= SS_CENTER;      // 居中文本
   }
   pStatic->Create(text, style, CRect(x, y, x + width, y + height), this, id);
   CStatic* pStatic = new CStatic();
   pStatic->Create(text, style, CRect(x, y, x + width, y + height), this);
   // 设置文本对齐方式
   pStatic->SetAlignment(alignment);
   // 动态设置字体大小
   CFont* pFont = GetOrCreateFont(height / 3);
   // 设置动态字体调整,并设置字体大小(动态字体会根据控件大小调整)
   int nSize = height / 3;
   CFont* pFont = GetOrCreateFont(nSize);
   pStatic->SetFont(pFont);
   pStatic->SetFontSize(nSize);
   pStatic->SetDynamicFont(TRUE);
   // 设置回调
   UserManager& userManager = UserManager::getInstance();
   UserRole enRole = userManager.getCurrentUserRole();
   if (clickCallback && (enRole == UserRole::SuperAdmin || enRole == UserRole::Operator)) {
      pStatic->SetClickCallback(clickCallback);
      pStatic->SetRoundedCorners(TRUE, 5);
      pStatic->DisableBorder();
   }
   // 存储控件指针
   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()
@@ -216,15 +274,29 @@
   int startIndex = (m_nCurrentPage - 1) * m_nRowsPerPage;
   int endIndex = min(startIndex + m_nRowsPerPage, static_cast<int>(m_displayData.size()));
   m_inputStates.clear();
   m_outputStates.clear();
   m_inputPLCAddresses.clear();
   m_outputPLCAddresses.clear();
   for (int i = 0; i < m_nRowsPerPage; ++i) {
      int row = i;
      if (startIndex + i < endIndex) {
         const auto& data = m_displayData[startIndex + i];
         // 添加状态到容器中
         m_inputStates.push_back(FALSE);      // 0 列
         m_outputStates.push_back(FALSE);   // 3 列
         // 添加 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);
@@ -234,6 +306,7 @@
         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);
@@ -243,8 +316,7 @@
      }
      else {
         // 隐藏这一行的所有控件
         for (int col = 0; col < m_nCols; ++col)
         {
         for (int col = 0; col < m_nCols; ++col) {
            m_staticControls[row * m_nCols + col]->ShowWindow(SW_HIDE);
         }
      }
@@ -255,10 +327,8 @@
void CIOMonitoringDlg::ClearDynamicControls()
{
   for (auto* pStatic : m_staticControls)
   {
      if (pStatic)
      {
   for (auto* pStatic : m_staticControls) {
      if (pStatic) {
         pStatic->DestroyWindow();
         delete pStatic;
      }
@@ -266,10 +336,229 @@
   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];
   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;
   }
   // 检查地址是否有效
   if (addr == 0 && hexAddress.CompareNoCase(_T("0")) != 0) {
      return false;
   }
   return true;
}
bool CIOMonitoringDlg::GeneratePLCAddress(MC::SOFT_COMPONENT component, int addr, CString& address, bool bHexFormat)
{
   // 根据组件类型生成前缀
   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;  // 如果是未知组件类型,返回失败
   }
   // 根据 bHexFormat 判断输出格式
   CString strAddr;
   if (bHexFormat) {
      strAddr.Format(_T("%X"), addr);  // 十六进制格式
   }
   else {
      strAddr.Format(_T("%d"), addr);  // 十进制格式
   }
   // 生成最终的地址字符串
   address = prefix + strAddr;
   return true;
}
// 处理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;
   }
   // 读取数据
   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 inputStartAddr, inputEndAddr;
   ParsePLCAddress(CString(startData.inputAddress.c_str()), startSoftComponent, inputStartAddr);
   ParsePLCAddress(CString(endData.inputAddress.c_str()), endSoftComponent, inputEndAddr);
   ASSERT(startSoftComponent == endSoftComponent);
   // 读取输入数据
   ReadPLCData(startSoftComponent, inputStartAddr, inputEndAddr, [this, inputStartAddr](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) {
      if (!::IsWindow(m_hWnd)) return;
      for (auto& data : m_displayData) {
         int nAddress;
         MC::SOFT_COMPONENT enComponent;
         if (ParsePLCAddress(CString(data.inputAddress.c_str()), enComponent, nAddress)) {
            int nOffset = nAddress - inputStartAddr;
            if (nOffset >= 0 && nOffset < (int)nDataSize) {
               int byteIndex = nOffset / 8;
               int bitIndex = nOffset % 8;
               data.bInputStates = CToolUnits::getBit(pData[byteIndex], bitIndex);
            }
         }
      }
   });
   // 读取输出数据
   int outputStartAddr, outputEndAddr;
   ParsePLCAddress(CString(startData.outputAddress.c_str()), startSoftComponent, outputStartAddr);
   ParsePLCAddress(CString(endData.outputAddress.c_str()), endSoftComponent, outputEndAddr);
   ASSERT(startSoftComponent == endSoftComponent);
   ReadPLCData(startSoftComponent, outputStartAddr, outputEndAddr, [this, outputStartAddr](IMcChannel* pChannel, int addr, char* pData, unsigned int nDataSize, int flag) {
      if (!::IsWindow(m_hWnd)) return;
      for (auto& data : m_displayData) {
         int nAddress;
         MC::SOFT_COMPONENT enComponent;
         if (ParsePLCAddress(CString(data.outputAddress.c_str()), enComponent, nAddress)) {
            int nOffset = nAddress - outputStartAddr;
            if (nOffset >= 0 && nOffset < (int)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];
         // 设置内容和背景颜色
         if (m_inputStates[i] != data.bInputStates)
         {
            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_inputStates[i] = data.bInputStates;
         }
         if (m_outputStates[i] != data.bOutputStates)
         {
            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));
            m_outputStates[i] = data.bOutputStates;
         }
      }
   }
}
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()
END_MESSAGE_MAP()
@@ -317,8 +606,8 @@
   }
   GetWindowRect(&dlgRect);
   int dlgWidth = dlgRect.Width() * 2;
   int dlgHeight = dlgRect.Height() * 2;
   int dlgWidth = dlgRect.Width() * 3;
   int dlgHeight = dlgRect.Height() * 3;
   if (dlgWidth > screenRect.Width()) {
      dlgWidth = screenRect.Width();
@@ -333,6 +622,12 @@
   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
@@ -364,6 +659,9 @@
   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()
@@ -376,4 +674,54 @@
   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);
      ReadPLCStates();
   }
   else if (TIMER_READ_UPDATE == nIDEvent) {
      ASSERT(m_pPLC);
      UpdatePLCStatesToUI();
   }
   CDialogEx::OnTimer(nIDEvent);
}
void CIOMonitoringDlg::OnClose()
{
   // TODO: 在此添加消息处理程序代码和/或调用默认值
   KillTimer(TIMER_READ_PLC_DATA);
   KillTimer(TIMER_READ_UPDATE);
   CDialogEx::OnClose();
}