// CEquipmentPage1.cpp: 实现文件 // #include "stdafx.h" #include "Servo.h" #include "CEquipmentPage1.h" #include "afxdialogex.h" // CEquipmentPage1 对话框 #define SIGNAL_GRID_ROWS 8 #define SIGNAL_GRID_COLS 8 #define SIGNAL_GRID_SIZE (SIGNAL_GRID_ROWS * SIGNAL_GRID_COLS) #define TIMER_ID_SIGNAL_UPDATE 1001 #define TIMER_INTERVAL_MS 1000 // 每 1 秒更新一次 IMPLEMENT_DYNAMIC(CEquipmentPage1, CHMPropertyPage) CEquipmentPage1::CEquipmentPage1(CWnd* pParent /*=nullptr*/) : CHMPropertyPage(IDD_PAGE_EQUIPMENT1, pParent) { m_pEquipment = nullptr; m_nCurrentDeviceID = 0; } CEquipmentPage1::~CEquipmentPage1() { } void CEquipmentPage1::DoDataExchange(CDataExchange* pDX) { CHMPropertyPage::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CEquipmentPage1, CHMPropertyPage) ON_WM_CTLCOLOR() ON_WM_DESTROY() ON_WM_SIZE() ON_WM_TIMER() END_MESSAGE_MAP() // CEquipmentPage1 消息处理程序 void CEquipmentPage1::OnApply() { __super::OnApply(); } void CEquipmentPage1::setEquipment(SERVO::CEquipment* pEquipment) { m_pEquipment = pEquipment; if (m_pEquipment != nullptr) { InitSignalListForDevice(pEquipment->getID()); } else { ResetSignalPanel(); } } void CEquipmentPage1::LoadSignalListFromCSV(const CString& strFilePath) { m_mapSignalListByID.clear(); CStdioFile file; if (!file.Open(strFilePath, CFile::modeRead | CFile::typeText)) { AfxMessageBox(_T("无法打开信号定义文件: ") + strFilePath); return; } CString strLine; int nLineNum = 0; while (file.ReadString(strLine)) { ++nLineNum; strLine.Trim(); if (strLine.IsEmpty() || strLine.Left(1) == _T("#") || strLine.Left(2) == _T("//")) { continue; // 跳过注释行或空行 } // 分割为 tokens std::vector tokens; int curPos = 0; CString token = strLine.Tokenize(_T(","), curPos); while (!token.IsEmpty()) { token.Trim(); // 去除每个字段的空格 tokens.push_back(token); token = strLine.Tokenize(_T(","), curPos); } // 格式检查 if (tokens.size() < 3) { TRACE(_T("CSV 格式错误 [行 %d]:字段数不足。\n"), nLineNum); continue; } // 解析字段 int nDeviceID = _ttoi(tokens[0]); CString strSignalName = tokens[1]; bool bClickable = _ttoi(tokens[2]) ? TRUE : FALSE; if (nDeviceID <= 0 || nDeviceID > 999 || strSignalName.IsEmpty()) { TRACE(_T("CSV 行 %d:无效设备ID=%d 或信号名为空\n"), nLineNum, nDeviceID); continue; } SignalInfo info = { strSignalName, false, bClickable }; m_mapSignalListByID[nDeviceID].push_back(info); } file.Close(); TRACE(_T("信号定义加载完成,共 %d 个设备。\n"), (int)m_mapSignalListByID.size()); } void CEquipmentPage1::InitSignalListForDevice(int nDeviceID) { m_nCurrentDeviceID = nDeviceID; m_vSignalList.clear(); auto it = m_mapSignalListByID.find(nDeviceID); if (it != m_mapSignalListByID.end()) { m_vSignalList = it->second; } else { TRACE(_T("Warning: No signals found for DeviceID=%d\n"), nDeviceID); } // 补齐到 64 项 while (m_vSignalList.size() < SIGNAL_GRID_SIZE) { m_vSignalList.push_back({ _T(""), false, false }); } InitSignalSlotTextAndClickable(); UpdateAllSignalStatesFromDevice(); } void CEquipmentPage1::UpdateSignalState(int nRow, int nCol, bool bNewState) { if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) { return; } int nIndex = nRow * SIGNAL_GRID_COLS + nCol; if (nIndex < 0 || nIndex >= static_cast(m_vSignalList.size())) { return; } if (m_vSignalList[nIndex].bCurrentState != bNewState) { m_vSignalList[nIndex].bCurrentState = bNewState; m_ctrlSignalPanel.SetSlotStatus(nRow, nCol, bNewState); TRACE(_T("[Device %d] UpdateSignalState: [%d, %d] = %d\n"), m_nCurrentDeviceID, nRow, nCol, bNewState); } } void CEquipmentPage1::InitSignalSlotTextAndClickable() { if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) { return; } for (int i = 0; i < SIGNAL_GRID_SIZE; ++i) { int nRow = i / SIGNAL_GRID_COLS; int nCol = i % SIGNAL_GRID_COLS; const auto& signal = m_vSignalList[i]; m_ctrlSignalPanel.SetSlotText(nRow, nCol, signal.strName); m_ctrlSignalPanel.SetSlotClickable(nRow, nCol, signal.bClickable); } } void CEquipmentPage1::UpdateAllSignalStatesFromDevice() { if (!m_pEquipment) { return; } for (int nRow = 0; nRow < SIGNAL_GRID_ROWS; ++nRow) { for (int nCol = 0; nCol < SIGNAL_GRID_COLS; ++nCol) { BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol); UpdateSignalState(nRow, nCol, bCurrentState); } } } void CEquipmentPage1::ResetSignalPanel() { if (!::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) { return; } m_ctrlSignalPanel.ClearAll(); m_vSignalList.clear(); m_nCurrentDeviceID = 0; } BOOL CEquipmentPage1::OnInitDialog() { CHMPropertyPage::OnInitDialog(); // TODO: 在此添加额外的初始化 m_ctrlSignalPanel.Create(AfxRegisterWndClass(0), _T("SignalGrid"), WS_CHILD | WS_VISIBLE, CRect(0, 0, 100, 100), this, 1002); m_ctrlSignalPanel.SetColors(RGB(0, 200, 0), RGB(220, 220, 220)); m_ctrlSignalPanel.SetGridSize(SIGNAL_GRID_ROWS, SIGNAL_GRID_COLS); m_ctrlSignalPanel.SetTextFont(_T("Microsoft YaHei"), 10); m_ctrlSignalPanel.SetSlotClickCallback([this](int nRow, int nCol) { int index = nRow * SIGNAL_GRID_COLS + nCol; if (index >= 0 && index < (int)m_vSignalList.size() && m_vSignalList[index].bClickable && m_pEquipment != nullptr) { // 读取当前状态并切换 const BOOL bCurrentState = m_pEquipment->isLinkSignalOn(nRow, nCol); m_pEquipment->setLinkSignal(nRow, nCol, !bCurrentState); } }); { TCHAR szPath[MAX_PATH] = {}; GetModuleFileName(nullptr, szPath, MAX_PATH); PathRemoveFileSpec(szPath); CString strCSVFile = CString(szPath) + _T("\\Config\\signals.csv"); LoadSignalListFromCSV(strCSVFile); } // 如果设备已设置,则初始化信号列表 if (m_pEquipment != nullptr) { InitSignalListForDevice(m_pEquipment->getID()); } KillTimer(TIMER_ID_SIGNAL_UPDATE); SetTimer(TIMER_ID_SIGNAL_UPDATE, TIMER_INTERVAL_MS, nullptr); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } HBRUSH CEquipmentPage1::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CHMPropertyPage::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何特性 // TODO: 如果默认的不是所需画笔,则返回另一个画笔 return hbr; } void CEquipmentPage1::OnDestroy() { CHMPropertyPage::OnDestroy(); // TODO: 在此处添加消息处理程序代码 KillTimer(TIMER_ID_SIGNAL_UPDATE); if (::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) { m_ctrlSignalPanel.DestroyWindow(); } } void CEquipmentPage1::OnSize(UINT nType, int cx, int cy) { CHMPropertyPage::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 if (::IsWindow(m_ctrlSignalPanel.GetSafeHwnd())) { CRect rc; GetClientRect(&rc); rc.DeflateRect(10, 10); m_ctrlSignalPanel.MoveWindow(rc); } } void CEquipmentPage1::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == TIMER_ID_SIGNAL_UPDATE) { if (m_pEquipment && !m_vSignalList.empty()) { UpdateAllSignalStatesFromDevice(); } } CHMPropertyPage::OnTimer(nIDEvent); }