// CPageGlassList.cpp: 实现文件 // #include "stdafx.h" #include "Servo.h" #include "CPageGlassList.h" #include "afxdialogex.h" #include "GlassJson.h" #include "CServoUtilsTool.h" #include "ToolUnits.h" #define PAGE_SIZE 10 #define PAGE_BACKGROUND_COLOR RGB(252, 252, 255) // CPageGlassList 对话框 IMPLEMENT_DYNAMIC(CPageGlassList, CDialogEx) CPageGlassList::CPageGlassList(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_PAGE_GLASS_LIST, pParent) { m_crBkgnd = PAGE_BACKGROUND_COLOR; m_hbrBkgnd = nullptr; m_pObserver = nullptr; m_strStatus = ""; m_nCurPage = 0; m_nTotalPages = 1; memset(m_szTimeStart, 0, sizeof(m_szTimeStart)); memset(m_szTimeEnd, 0, sizeof(m_szTimeEnd)); m_szTimeStart[0] = '\0'; m_szTimeEnd[0] = '\0'; } CPageGlassList::~CPageGlassList() { if (m_hbrBkgnd != nullptr) { ::DeleteObject(m_hbrBkgnd); } if (m_pObserver != nullptr) { m_pObserver->unsubscribe(); m_pObserver = nullptr; } } void CPageGlassList::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_DATETIMEPICKER_START, m_dateTimeStart); DDX_Control(pDX, IDC_DATETIMEPICKER_END, m_dateTimeEnd); DDX_Control(pDX, IDC_LIST_ALARM, m_listCtrl); } BEGIN_MESSAGE_MAP(CPageGlassList, CDialogEx) ON_WM_CTLCOLOR() ON_WM_DESTROY() ON_WM_SIZE() ON_WM_TIMER() ON_CBN_SELCHANGE(IDC_COMBO_DATETIME, &CPageGlassList::OnCbnSelchangeComboDatetime) ON_CBN_SELCHANGE(IDC_COMBO_STATUS_FILTER, &CPageGlassList::OnCbnSelchangeComboStatusFilter) ON_BN_CLICKED(IDC_BUTTON_SEARCH, &CPageGlassList::OnBnClickedButtonSearch) ON_BN_CLICKED(IDC_BUTTON_EXPORT, &CPageGlassList::OnBnClickedButtonExport) ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CPageGlassList::OnBnClickedButtonPrevPage) ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CPageGlassList::OnBnClickedButtonNextPage) END_MESSAGE_MAP() // CPageGlassList 消息处理程序 void CPageGlassList::InitRxWindow() { /* code */ // 订阅数据 IRxWindows* pRxWindows = RX_GetRxWindows(); pRxWindows->enableLog(5); if (m_pObserver == NULL) { m_pObserver = pRxWindows->allocObserver([&](IAny* pAny) -> void { // onNext pAny->addRef(); int code = pAny->getCode(); if (RX_CODE_EQ_ROBOT_TASK == code) { UpdatePageData(); } pAny->release(); }, [&]() -> void { // onComplete }, [&](IThrowable* pThrowable) -> void { // onErrorm pThrowable->printf(); }); theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())->subscribe(m_pObserver); } } void CPageGlassList::Resize() { CRect rcClient; GetClientRect(&rcClient); // ===== 常量定义 ===== const int nLeft = 12; const int nRight = 12; const int nTop = 58; const int nButtonHeight = 28; const int nButtonMarginBottom = 12; const int nSpacing = 8; const int nButtonWidth = 80; const int nLabelWidth = 100; // ===== 分页控件布局 ===== int yBottom = rcClient.bottom - nButtonMarginBottom - nButtonHeight; int xRight = rcClient.Width() - nRight; CWnd* pBtnNext = GetDlgItem(IDC_BUTTON_NEXT_PAGE); CWnd* pBtnPrev = GetDlgItem(IDC_BUTTON_PREV_PAGE); CWnd* pLabelPage = GetDlgItem(IDC_LABEL_PAGE_NUMBER); if (pBtnNext && pBtnPrev && pLabelPage) { // 获取分页文本宽度估算 //CString strLabel; //GetDlgItemText(IDC_LABEL_PAGE_NUMBER, strLabel); //if (strLabel.IsEmpty()) { // strLabel = _T("第 1 / 1 页"); //} //int nCharWidth = 8; //int nLabelWidth = strLabel.GetLength() * nCharWidth + 20; // 设置按钮和标签位置 pBtnNext->MoveWindow(xRight - nButtonWidth, yBottom, nButtonWidth, nButtonHeight); xRight -= nButtonWidth + nSpacing; pLabelPage->MoveWindow(xRight - nLabelWidth, yBottom, nLabelWidth, nButtonHeight); xRight -= nLabelWidth + nSpacing; pBtnPrev->MoveWindow(xRight - nButtonWidth, yBottom, nButtonWidth, nButtonHeight); } // ===== 表格区域布局 ===== if (nullptr != m_listCtrl.m_hWnd) { int listHeight = yBottom - nTop - nSpacing; m_listCtrl.MoveWindow(nLeft, nTop, rcClient.Width() - nLeft - nRight, listHeight); } } void CPageGlassList::InitStatusCombo() { CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_STATUS_FILTER); if (nullptr != pComboBox) { pComboBox->ResetContent(); pComboBox->AddString(_T("全部")); pComboBox->AddString(_T("Ready")); pComboBox->AddString(_T("Running")); pComboBox->AddString(_T("Error")); pComboBox->AddString(_T("Abort")); pComboBox->AddString(_T("Completed")); pComboBox->SetCurSel(0); } } void CPageGlassList::InitTimeRangeCombo() { CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME); if (nullptr != pComboBox) { pComboBox->ResetContent(); pComboBox->AddString(_T("不限")); pComboBox->AddString(_T("今天")); pComboBox->AddString(_T("七天内")); pComboBox->AddString(_T("本月")); pComboBox->AddString(_T("今年")); pComboBox->AddString(_T("自定义")); pComboBox->SetCurSel(0); } } void CPageGlassList::InitDateTimeControls() { if (m_dateTimeStart.m_hWnd == nullptr || m_dateTimeEnd.m_hWnd == nullptr) { return; } // 禁用初始状态 m_dateTimeStart.EnableWindow(FALSE); m_dateTimeEnd.EnableWindow(FALSE); // 设置格式:显示日期 + 时间 //m_dateTimeStart.SetFormat(_T("yyyy/MM/dd HH:mm:ss")); //m_dateTimeEnd.SetFormat(_T("yyyy/MM/dd HH:mm:ss")); // 修改样式以支持时间格式 //DWORD dwStyleStart = m_dateTimeStart.GetStyle(); //DWORD dwStyleEnd = m_dateTimeEnd.GetStyle(); //m_dateTimeStart.ModifyStyle(0, DTS_TIMEFORMAT | DTS_UPDOWN); //m_dateTimeEnd.ModifyStyle(0, DTS_TIMEFORMAT); } void CPageGlassList::LoadData() { m_nCurPage = 1; UpdatePageData(); } void CPageGlassList::UpdatePageData() { // 查询 auto& db = GlassLogDb::Instance(); auto page = db.queryPaged(m_filters, PAGE_SIZE, PAGE_SIZE * (m_nCurPage - 1)); m_listCtrl.DeleteAllItems(); for (const auto& r : page.items) { int index = m_listCtrl.InsertItem(m_listCtrl.GetItemCount(), std::to_string(r.id).c_str()); m_listCtrl.SetItemText(index, 1, std::to_string(r.cassetteSeqNo).c_str()); m_listCtrl.SetItemText(index, 2, std::to_string(r.jobSeqNo).c_str()); m_listCtrl.SetItemText(index, 3, r.classId.c_str()); m_listCtrl.SetItemText(index, 4, SERVO::CServoUtilsTool::getMaterialsTypeText((SERVO::MaterialsType)r.materialType).c_str()); m_listCtrl.SetItemText(index, 5, SERVO::CServoUtilsTool::getGlassStateText((SERVO::GlsState)r.state).c_str()); m_listCtrl.SetItemText(index, 6, r.tStart.c_str()); m_listCtrl.SetItemText(index, 7, r.tEnd.c_str()); m_listCtrl.SetItemText(index, 8, r.buddyId.c_str()); m_listCtrl.SetItemText(index, 9, SERVO::CServoUtilsTool::getInspResultText((SERVO::InspResult)r.aoiResult).c_str()); m_listCtrl.SetItemText(index, 10, r.path.c_str()); m_listCtrl.SetItemText(index, 11, r.params.c_str()); // 测试反序列化 /* SERVO::CGlass g2; std::string err; if (GlassJson::FromString(r.pretty, g2, &err)) { AfxMessageBox(r.pretty.c_str()); } */ } // 上一页 / 下一页 UpdatePageControls(); } void CPageGlassList::UpdatePageControls() { CString strPage; strPage.Format(_T("第 %d / %d 页"), m_nCurPage, m_nTotalPages); SetDlgItemText(IDC_LABEL_PAGE_NUMBER, strPage); GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurPage > 1); GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurPage < m_nTotalPages); } // CPageTransferLog 消息处理程序 BOOL CPageGlassList::OnInitDialog() { CDialogEx::OnInitDialog(); // TODO: 在此添加额外的初始化 SetTimer(1, 3000, nullptr); // 下拉框控件 InitStatusCombo(); InitTimeRangeCombo(); // 日期控件 InitDateTimeControls(); // 报表控件 CString strIniFile, strItem; strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); DWORD dwStyle = m_listCtrl.GetExtendedStyle(); dwStyle |= LVS_EX_FULLROWSELECT; dwStyle |= LVS_EX_GRIDLINES; m_listCtrl.SetExtendedStyle(dwStyle); HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1); ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL); CString headers[] = { _T("id"), _T("Cassette Sequence No"), _T("Job Sequence No"), _T("Class ID"), _T("物料类型"), _T("状态"), _T("工艺开始时间"), _T("工艺结束时间"), _T("邦定Glass ID"), _T("AOI检测结果"), _T("路径"), _T("工艺参数") }; int widths[] = { 80, 80, 80, 100, 120, 120, 120, 120, 200, 200, 200, 200 }; for (int i = 0; i < _countof(headers); ++i) { strItem.Format(_T("Col_%d_Width"), i); widths[i] = GetPrivateProfileInt("GlassListCtrl", strItem, widths[i], strIniFile); m_listCtrl.InsertColumn(i, headers[i], LVCFMT_LEFT, widths[i]); } Resize(); OnBnClickedButtonSearch(); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } HBRUSH CPageGlassList::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { if (nCtlColor == CTLCOLOR_STATIC) { pDC->SetBkColor(m_crBkgnd); } if (m_hbrBkgnd == nullptr) { m_hbrBkgnd = CreateSolidBrush(m_crBkgnd); } return m_hbrBkgnd; } void CPageGlassList::OnDestroy() { CDialogEx::OnDestroy(); if (m_hbrBkgnd != nullptr) { ::DeleteObject(m_hbrBkgnd); m_hbrBkgnd = nullptr; } if (m_pObserver != nullptr) { m_pObserver->unsubscribe(); m_pObserver = nullptr; } // 保存列宽 CString strIniFile, strItem, strTemp; strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); CHeaderCtrl* pHeader = m_listCtrl.GetHeaderCtrl(); for (int i = 0; i < pHeader->GetItemCount(); i++) { RECT rect; pHeader->GetItemRect(i, &rect); strItem.Format(_T("Col_%d_Width"), i); strTemp.Format(_T("%d"), rect.right - rect.left); WritePrivateProfileString("GlassListCtrl", strItem, strTemp, strIniFile); } } void CPageGlassList::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); Resize(); } void CPageGlassList::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { KillTimer(1); InitRxWindow(); } CDialogEx::OnTimer(nIDEvent); } void CPageGlassList::OnCbnSelchangeComboDatetime() { CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME); int nIndex = pComboBox->GetCurSel(); int nCount = pComboBox->GetCount(); m_dateTimeStart.EnableWindow(nIndex == nCount - 1); m_dateTimeEnd.EnableWindow(nIndex == nCount - 1); // 更新日期过滤器和页面数据 // UpdateDateFilter(); // LoadTransfers(); } void CPageGlassList::OnCbnSelchangeComboStatusFilter() { CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_STATUS_FILTER); int nIndex = pComboBox->GetCurSel(); if (nIndex == 0) { m_strStatus.clear(); } else { CString cstrText; pComboBox->GetLBText(nIndex, cstrText); m_strStatus = CT2A(cstrText); } // LoadTransfers(); } void CPageGlassList::OnBnClickedButtonSearch() { // 获取关键字输入框内容 CString strKeyword; GetDlgItemText(IDC_EDIT_KEYWORD, strKeyword); m_filters.keyword = CT2A(strKeyword); CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME); int index = pComboBox->GetCurSel(); if (index == 0) { // 不限 m_filters.tStartFrom = std::nullopt; m_filters.tStartTo = std::nullopt; } else if (index == 1) { auto [fromUtc, toUtc] = CToolUnits::CalcQuickRangeUtc(QuickRange::Today); m_filters.tStartFrom = fromUtc; m_filters.tStartTo = toUtc; } else if (index == 2) { auto [fromUtc, toUtc] = CToolUnits::CalcQuickRangeUtc(QuickRange::Last7Days); m_filters.tStartFrom = fromUtc; m_filters.tStartTo = toUtc; } else if (index == 3) { auto [fromUtc, toUtc] = CToolUnits::CalcQuickRangeUtc(QuickRange::ThisMonth); m_filters.tStartFrom = fromUtc; m_filters.tStartTo = toUtc; } else if (index == 4) { auto [fromUtc, toUtc] = CToolUnits::CalcQuickRangeUtc(QuickRange::ThisYear); m_filters.tStartFrom = fromUtc; m_filters.tStartTo = toUtc; } else if(index == 5){ // 自定义 std::chrono::system_clock::time_point tp; if (CToolUnits::GetCtrlDateRangeUtc_StartOfDay(m_dateTimeStart, tp)) m_filters.tStartFrom = tp; if (CToolUnits::GetCtrlDateRangeUtc_EndOfDay(m_dateTimeEnd, tp)) m_filters.tStartTo = tp; } auto& db = GlassLogDb::Instance(); long long total = db.count(m_filters); m_nTotalPages = (PAGE_SIZE > 0) ? int((total + PAGE_SIZE - 1) / PAGE_SIZE) : 1; LoadData(); } void CPageGlassList::OnBnClickedButtonExport() { CFileDialog fileDialog(FALSE, _T("csv"), NULL, OFN_HIDEREADONLY, _T("CSV Files (*.csv)|*.csv||")); if (fileDialog.DoModal() != IDOK) { return; } // 导出 CSV:导出符合 filters 的“全部记录”(不受分页限制) // 返回导出的行数(不含表头) // csvPath:目标文件路径(UTF-8) auto& db = GlassLogDb::Instance(); std::string csvPath((LPTSTR)(LPCTSTR)fileDialog.GetPathName()); if (db.exportCsv(csvPath, m_filters) > 0) { AfxMessageBox("导出CSV成功!"); } } void CPageGlassList::OnBnClickedButtonPrevPage() { if (m_nCurPage > 1) { m_nCurPage--; UpdatePageData(); } } void CPageGlassList::OnBnClickedButtonNextPage() { if (m_nCurPage < m_nTotalPages) { m_nCurPage++; UpdatePageData(); } }