Merge branch 'master' into clh
# Conflicts:
# SourceCode/Bond/Servo/resource.h
| | |
| | | hFont = (HFONT)pFont->GetSafeHandle(); |
| | | } |
| | | |
| | | |
| | | ::SelectObject(hDC, hFont); |
| | | HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont); |
| | | ::SetBkMode(hDC, TRANSPARENT); |
| | | ::SetTextColor(hDC, m_crText[state]); |
| | | |
| | |
| | | } |
| | | DrawTextA(hDC, szText, (int)strlen(szText), &rcText, DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_END_ELLIPSIS); |
| | | } |
| | | ::SelectObject(hDC, hOldFont); |
| | | |
| | | |
| | | // 是否有小圆点 |
| | |
| | | ::Polygon(hDC, pt, 3); |
| | | ::SelectObject(hDC, hOldBrush); |
| | | ::SelectObject(hDC, hOldPen); |
| | | ::DeleteObject(hBrush); |
| | | ::DeleteObject(hPen); |
| | | ::DeleteObject(hbrDrop); // 正确释放小三角使用的画刷 |
| | | ::DeleteObject(hPenDrop); // 正确释放小三角的笔 |
| | | } |
| | | } |
| | | |
| | |
| | | return m_robotCmdParam.putSlotNo; |
| | | } |
| | | |
| | | CString CRobotTask::getStateString() |
| | | { |
| | | switch (m_state) { |
| | | case ROBOT_TASK_STATE::Ready: return _T("Ready"); |
| | | case ROBOT_TASK_STATE::Running: return _T("Running"); |
| | | case ROBOT_TASK_STATE::Error: return _T("Error"); |
| | | case ROBOT_TASK_STATE::Abort: return _T("Abort"); |
| | | case ROBOT_TASK_STATE::Completed: return _T("Completed"); |
| | | default: return _T("Unknown"); |
| | | } |
| | | } |
| | | |
| | | void CRobotTask::fetchOut() |
| | | { |
| | | m_timeFetchOut = CToolUnits::getUnixTimestamp();; |
| | |
| | | int getSrcSlot(); |
| | | int getTarPosition(); |
| | | int getTarSlot(); |
| | | CString getStateString(); |
| | | |
| | | // 从源地拔片 |
| | | void fetchOut(); |
| | |
| | | ON_WM_DESTROY() |
| | | ON_WM_SIZE() |
| | | ON_WM_ACTIVATE() |
| | | ON_BN_CLICKED(IDC_BUTTON_ABORT_TASK, &CRobotTaskDlg::OnBnClickedAbortTask) |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | GetDlgItem(IDC_LABEL_NO_TASK)->ShowWindow(m_pRobotTask == nullptr ? SW_SHOW : SW_HIDE); |
| | | GetDlgItem(IDC_LABEL_GET_PUT)->ShowWindow(m_pRobotTask != nullptr ? SW_SHOW : SW_HIDE); |
| | | |
| | | if (m_btnAbortTask.m_hWnd) { |
| | | m_btnAbortTask.ShowWindow(m_pRobotTask ? SW_SHOW : SW_HIDE); |
| | | } |
| | | |
| | | if (m_pRobotTask != nullptr) { |
| | | SERVO::CEquipment* pEq1, * pEq2; |
| | | pEq1 = theApp.m_model.getMaster().getEquipment(m_pRobotTask->getSrcPosition()); |
| | | pEq2 = theApp.m_model.getMaster().getEquipment(m_pRobotTask->getTarPosition()); |
| | | using namespace SERVO; |
| | | |
| | | CString strText; |
| | | strText.Format(_T("%s --> %s"), pEq1->getName().c_str(), pEq2->getName().c_str()); |
| | | SetDlgItemText(IDC_LABEL_GET_PUT, strText); |
| | | CEquipment* pSrcEq = theApp.m_model.getMaster().getEquipment(pRobotTask->getSrcPosition()); |
| | | CEquipment* pDstEq = theApp.m_model.getMaster().getEquipment(pRobotTask->getTarPosition()); |
| | | |
| | | ROBOT_CMD_PARAM& param = pRobotTask->getRobotCmdParam(); |
| | | |
| | | auto format_time = [](time_t t) -> CString { |
| | | if (t == 0) { |
| | | return _T("-"); |
| | | } |
| | | CTime time(t); |
| | | return time.Format(_T("%Y-%m-%d %H:%M:%S")); |
| | | }; |
| | | |
| | | CString strDetail; |
| | | strDetail.Format( |
| | | _T("任务 ID: %s\r\n源位置: %s (P%d)\r\n目标位置: %s (P%d)\r\n") |
| | | _T("源槽位: Slot %d\r\n目标槽位: Slot %d\r\n手臂编号: Arm %d\r\n任务状态: %s\r\n") |
| | | _T("创建时间: %s\r\n取片时间: %s\r\n放片时间: %s\r\n结束时间: %s"), |
| | | pRobotTask->getId().c_str(), |
| | | pSrcEq ? pSrcEq->getName().c_str() : _T("未知"), |
| | | param.getPosition, |
| | | pDstEq ? pDstEq->getName().c_str() : _T("未知"), |
| | | param.putPosition, |
| | | param.getSlotNo, |
| | | param.putSlotNo, |
| | | param.armNo, |
| | | pRobotTask->getStateString(), |
| | | format_time(pRobotTask->getCreateTime()), |
| | | format_time(pRobotTask->getFetchoutTime()), |
| | | format_time(pRobotTask->getStoredTime()), |
| | | format_time(pRobotTask->getFinishTime()) |
| | | ); |
| | | |
| | | SetDlgItemText(IDC_LABEL_GET_PUT, strDetail); |
| | | } |
| | | } |
| | | |
| | |
| | | CDialogEx::OnInitDialog(); |
| | | |
| | | // TODO: 在此添加额外的初始化 |
| | | // 创建“停止任务”按钮 |
| | | m_btnAbortTask.Create(_T("停止任务"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(0, 0, 100, 30), this, IDC_BUTTON_ABORT_TASK); |
| | | |
| | | // 动态按钮创建后设置字体 |
| | | if (m_fontButton.GetSafeHandle() == nullptr) { |
| | | m_fontButton.CreatePointFont(110, _T("微软雅黑")); // 或 "Segoe UI" |
| | | } |
| | | m_btnAbortTask.SetFont(&m_fontButton); |
| | | |
| | | // 设置 LABEL 控件的字体 |
| | | if (m_fontDetail.GetSafeHandle() == nullptr) { |
| | | m_fontDetail.CreatePointFont(100, _T("微软雅黑")); |
| | | } |
| | | GetDlgItem(IDC_LABEL_GET_PUT)->SetFont(&m_fontDetail); |
| | | |
| | | return TRUE; // return TRUE unless you set the focus to a control |
| | | // 异常: OCX 属性页应返回 FALSE |
| | |
| | | CDialogEx::OnDestroy(); |
| | | |
| | | // TODO: 在此处添加消息处理程序代码 |
| | | if (m_fontButton.GetSafeHandle()) { |
| | | m_fontButton.DeleteObject(); |
| | | } |
| | | |
| | | if (m_fontDetail.GetSafeHandle()) { |
| | | m_fontDetail.DeleteObject(); |
| | | } |
| | | } |
| | | |
| | | void CRobotTaskDlg::OnSize(UINT nType, int cx, int cy) |
| | |
| | | (rcClient.Height() - rcItem.Height()) / 2, rcItem.Width(), rcItem.Height()); |
| | | |
| | | pItem = GetDlgItem(IDC_LABEL_GET_PUT); |
| | | pItem->GetClientRect(&rcItem); |
| | | pItem->MoveWindow(12, |
| | | 12, rcItem.Width(), rcItem.Height()); |
| | | if (pItem && pItem->m_hWnd) { |
| | | const int nLabelX = 12; |
| | | const int nLabelY = 12; |
| | | const int nLabelWidth = rcClient.Width() - 24; |
| | | const int nLabelHeight = rcClient.Height() - 24; |
| | | |
| | | pItem->MoveWindow(nLabelX, nLabelY, nLabelWidth, nLabelHeight); |
| | | } |
| | | |
| | | // 设置“停止任务”按钮位置(右下角) |
| | | if (m_btnAbortTask.m_hWnd != nullptr) { |
| | | const int nBtnWidth = 100; |
| | | const int nBtnHeight = 28; |
| | | const int nMargin = 12; |
| | | |
| | | const int nPosX = rcClient.right - nBtnWidth - nMargin; |
| | | const int nPosY = rcClient.bottom - nBtnHeight - nMargin; |
| | | |
| | | m_btnAbortTask.MoveWindow(nPosX, nPosY, nBtnWidth, nBtnHeight); |
| | | } |
| | | } |
| | | |
| | | void CRobotTaskDlg::OnBnClickedAbortTask() |
| | | { |
| | | if (m_pRobotTask) { |
| | | m_pRobotTask->abort(); |
| | | AfxMessageBox(_T("任务已停止。")); |
| | | } |
| | | } |
| | |
| | | |
| | | private: |
| | | SERVO::CRobotTask* m_pRobotTask; |
| | | CButton m_btnAbortTask; |
| | | CFont m_fontButton; |
| | | CFont m_fontDetail; |
| | | |
| | | |
| | | // 对话框数据 |
| | |
| | | afx_msg void OnDestroy(); |
| | | afx_msg void OnSize(UINT nType, int cx, int cy); |
| | | afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); |
| | | afx_msg void OnBnClickedAbortTask(); |
| | | }; |
| | |
| | | BEGIN_MESSAGE_MAP(CJobSlotGrid, CWnd) |
| | | ON_WM_PAINT() |
| | | ON_WM_ERASEBKGND() |
| | | ON_WM_MOUSEMOVE() |
| | | ON_WM_MOUSELEAVE() |
| | | ON_WM_LBUTTONDOWN() |
| | | ON_WM_LBUTTONUP() |
| | | END_MESSAGE_MAP() |
| | | |
| | | CJobSlotGrid::CJobSlotGrid() { |
| | |
| | | m_nRows = nRows; |
| | | m_nCols = nCols; |
| | | m_vSlotStatus.assign(nRows, std::vector<bool>(nCols, false)); |
| | | m_vSlotClickable.assign(nRows, std::vector<bool>(nCols, false)); |
| | | |
| | | // 初始化文本数组 |
| | | m_vSlotText.assign(nRows, std::vector<CString>(nCols)); |
| | |
| | | Invalidate(); |
| | | } |
| | | |
| | | void CJobSlotGrid::SetSlotClickable(int nRow, int nCol, bool bClickable) |
| | | { |
| | | if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) { |
| | | m_vSlotClickable[nRow][nCol] = bClickable; |
| | | } |
| | | } |
| | | |
| | | bool CJobSlotGrid::IsSlotClickable(int nRow, int nCol) const |
| | | { |
| | | if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) { |
| | | return m_vSlotClickable[nRow][nCol]; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | void CJobSlotGrid::SetSlotClickCallback(SlotClickCallback fnCallback) |
| | | { |
| | | m_fnSlotClickCallback = fnCallback; |
| | | } |
| | | |
| | | BOOL CJobSlotGrid::OnEraseBkgnd(CDC* pDC) { |
| | | return TRUE; |
| | | } |
| | | |
| | | void CJobSlotGrid::OnMouseMove(UINT nFlags, CPoint point) |
| | | { |
| | | TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd }; |
| | | ::TrackMouseEvent(&tme); |
| | | |
| | | CRect rect; |
| | | GetClientRect(&rect); |
| | | int nCellWidth = rect.Width() / m_nCols; |
| | | int nCellHeight = rect.Height() / m_nRows; |
| | | |
| | | int nCol = point.x / nCellWidth; |
| | | int nRow = point.y / nCellHeight; |
| | | |
| | | if (nRow != m_ptHover.y || nCol != m_ptHover.x) { |
| | | m_ptHover = CPoint(nCol, nRow); |
| | | Invalidate(); |
| | | } |
| | | |
| | | CWnd::OnMouseMove(nFlags, point); |
| | | } |
| | | |
| | | void CJobSlotGrid::OnMouseLeave() |
| | | { |
| | | m_ptHover = CPoint(-1, -1); |
| | | Invalidate(); |
| | | |
| | | CWnd::OnMouseLeave(); |
| | | } |
| | | |
| | | void CJobSlotGrid::OnLButtonDown(UINT nFlags, CPoint point) |
| | | { |
| | | m_bLButtonDown = true; |
| | | Invalidate(); |
| | | |
| | | CWnd::OnLButtonDown(nFlags, point); |
| | | } |
| | | |
| | | void CJobSlotGrid::OnLButtonUp(UINT nFlags, CPoint point) |
| | | { |
| | | m_bLButtonDown = false; |
| | | Invalidate(); |
| | | |
| | | // 保持原有逻辑不变 |
| | | CRect rect; |
| | | GetClientRect(&rect); |
| | | int nCellWidth = rect.Width() / m_nCols; |
| | | int nCellHeight = rect.Height() / m_nRows; |
| | | |
| | | int nCol = point.x / nCellWidth; |
| | | int nRow = point.y / nCellHeight; |
| | | |
| | | if (IsSlotClickable(nRow, nCol)) { |
| | | if (m_fnSlotClickCallback) { |
| | | m_fnSlotClickCallback(nRow, nCol); |
| | | } |
| | | } |
| | | |
| | | CWnd::OnLButtonUp(nFlags, point); |
| | | } |
| | | |
| | | void CJobSlotGrid::OnPaint() { |
| | |
| | | for (int j = 0; j < m_nCols; ++j) { |
| | | CRect cellRect(j * nCellWidth, i * nCellHeight, (j + 1) * nCellWidth, (i + 1) * nCellHeight); |
| | | |
| | | // 背景 |
| | | CBrush* pBrush = m_vSlotStatus[i][j] ? &m_brushHasJob : &m_brushNoJob; |
| | | pDC->FillRect(&cellRect, pBrush); |
| | | // 判断状态:悬停 / 按下 |
| | | bool bIsHover = (m_ptHover.x == j && m_ptHover.y == i); |
| | | bool bIsClicking = bIsHover && m_bLButtonDown; |
| | | |
| | | // 选择颜色 |
| | | COLORREF fillColor; |
| | | if (bIsClicking) { |
| | | fillColor = RGB(0, 120, 215); // 鼠标按下色 |
| | | } |
| | | else if (bIsHover) { |
| | | fillColor = RGB(200, 230, 255); // 悬停高亮 |
| | | } |
| | | else { |
| | | fillColor = m_vSlotStatus[i][j] ? m_colorHasJob : m_colorNoJob; |
| | | } |
| | | |
| | | // 画背景 |
| | | CBrush brush(fillColor); |
| | | pDC->FillRect(&cellRect, &brush); |
| | | |
| | | // 边框 |
| | | pDC->DrawEdge(&cellRect, EDGE_SUNKEN, BF_RECT); |
| | | |
| | | // 文字(居中) |
| | | // 文本 |
| | | pDC->SetBkMode(TRANSPARENT); |
| | | pDC->SetTextColor(RGB(0, 0, 0)); |
| | | pDC->DrawText(m_vSlotText[i][j], &cellRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); |
| | |
| | | #pragma once |
| | | #include <afxwin.h> |
| | | #include <vector> |
| | | #include <functional> |
| | | |
| | | using SlotClickCallback = std::function<void(int nRow, int nCol)>; |
| | | |
| | | class CJobSlotGrid : public CWnd |
| | | { |
| | |
| | | void SetSlotText(int nRow, int nCol, const CString& strText); |
| | | void SetTextFont(const CString& strFontName, int nPointSize); |
| | | void ClearAll(); |
| | | void SetSlotClickable(int nRow, int nCol, bool bClickable); |
| | | bool IsSlotClickable(int nRow, int nCol) const; |
| | | void SetSlotClickCallback(SlotClickCallback fnCallback); |
| | | |
| | | protected: |
| | | afx_msg void OnPaint(); |
| | | afx_msg BOOL OnEraseBkgnd(CDC* pDC); |
| | | afx_msg void OnMouseMove(UINT nFlags, CPoint point); |
| | | afx_msg void OnMouseLeave(); |
| | | afx_msg void OnLButtonDown(UINT nFlags, CPoint point); |
| | | afx_msg void OnLButtonUp(UINT nFlags, CPoint point); |
| | | DECLARE_MESSAGE_MAP() |
| | | |
| | | private: |
| | | int m_nRows; |
| | | int m_nCols; |
| | | bool m_bLButtonDown = false; // 鼠标是否按下 |
| | | CPoint m_ptHover{ -1, -1 }; // 当前悬停的 cell 索引(row, col) |
| | | CFont m_fontText; |
| | | COLORREF m_colorHasJob; |
| | | COLORREF m_colorNoJob; |
| | |
| | | CBrush m_brushNoJob; |
| | | std::vector<std::vector<bool>> m_vSlotStatus; |
| | | std::vector<std::vector<CString>> m_vSlotText; |
| | | std::vector<std::vector<bool>> m_vSlotClickable; |
| | | SlotClickCallback m_fnSlotClickCallback; |
| | | |
| | | void DrawGrid(CDC* pDC); |
| | | }; |