| | |
| | | |
| | | // 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() |
| | |
| | | ON_WM_CTLCOLOR() |
| | | ON_WM_DESTROY() |
| | | ON_WM_SIZE() |
| | | ON_WM_TIMER() |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | 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<CString> 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<int>(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->isLinkSignalUpstreamOn(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->isLinkSignalUpstreamOn(nRow, nCol); |
| | | m_pEquipment->setLinkSignalUpstream(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 |
| | |
| | | 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); |
| | | } |