| | |
| | | #include "Servo.h" |
| | | #include "ServoDlg.h" |
| | | #include "afxdialogex.h" |
| | | #include "Common.h" |
| | | #include "Log.h" |
| | | #include "SecsTestDlg.h" |
| | | #include <chrono> |
| | | #include <thread> |
| | | #include <cmath> |
| | | |
| | | |
| | | #ifdef _DEBUG |
| | | #define new DEBUG_NEW |
| | | #endif |
| | | |
| | | // Image |
| | | #define IMAGE_ROBOT 2 |
| | | |
| | | #define INDICATE_BONDER1 1 |
| | | #define INDICATE_BONDER2 2 |
| | |
| | | : CDialogEx(IDD_SERVO_DIALOG, pParent) |
| | | { |
| | | m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); |
| | | m_crBkgnd = RGB(255, 255, 255); |
| | | m_crBkgnd = APPDLG_BACKGROUND_COLOR; |
| | | m_hbrBkgnd = nullptr; |
| | | m_bShowLogWnd = FALSE; |
| | | m_bIsRobotMoving = FALSE; |
| | | m_pLogDlg = nullptr; |
| | | } |
| | | |
| | | void CServoDlg::DoDataExchange(CDataExchange* pDX) |
| | |
| | | ON_WM_CTLCOLOR() |
| | | ON_WM_DESTROY() |
| | | ON_BN_CLICKED(IDC_BUTTON_LOG, &CServoDlg::OnBnClickedButtonLog) |
| | | ON_WM_SIZE() |
| | | ON_WM_CLOSE() |
| | | ON_MESSAGE(ID_MSG_LOGDLG_HIDE, &CServoDlg::OnLogDlgHide) |
| | | ON_WM_MOVING() |
| | | ON_WM_MOVE() |
| | | ON_COMMAND(ID_MENU_FILE_EXIT, &CServoDlg::OnMenuFileExit) |
| | | ON_UPDATE_COMMAND_UI(ID_MENU_FILE_EXIT, &CServoDlg::OnUpdateMenuFileExit) |
| | | ON_COMMAND(ID_MENU_FILE_SETTINGS, &CServoDlg::OnMenuFileSettings) |
| | | ON_UPDATE_COMMAND_UI(ID_MENU_FILE_SETTINGS, &CServoDlg::OnUpdateMenuFileSettings) |
| | | ON_COMMAND(ID_MENU_FILE_SECSTEST, &CServoDlg::OnMenuFileSecsTest) |
| | | ON_UPDATE_COMMAND_UI(ID_MENU_FILE_SECSTEST, &CServoDlg::OnUpdateMenuFileSecsTest) |
| | | ON_COMMAND(ID_MENU_WND_LOG, &CServoDlg::OnMenuWndLog) |
| | | ON_UPDATE_COMMAND_UI(ID_MENU_WND_LOG, &CServoDlg::OnUpdateMenuWndLog) |
| | | ON_COMMAND(ID_MENU_HELP_ABOUT, &CServoDlg::OnMenuHelpAbout) |
| | | ON_WM_INITMENUPOPUP() |
| | | ON_WM_ERASEBKGND() |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | SetIcon(m_hIcon, FALSE); // 设置小图标 |
| | | |
| | | |
| | | // 菜单 |
| | | CMenu menu; |
| | | menu.LoadMenu(IDR_MENU_APP); |
| | | SetMenu(&menu); |
| | | |
| | | |
| | | |
| | | // ͼʾ |
| | | m_pGraph = CServoGraph::Hook(GetDlgItem(IDC_SERVO_GRAPH1)->GetSafeHwnd()); |
| | | CString strPath; |
| | | strPath.Format(_T("%s\\res\\Servo001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); |
| | | m_pGraph->AddImage(1, (LPTSTR)(LPCTSTR)strPath, 0, 0); |
| | | |
| | | strPath.Format(_T("%s\\res\\Robot001.bmp"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); |
| | | m_pGraph->AddImage(IMAGE_ROBOT, (LPTSTR)(LPCTSTR)strPath, 170, 270); |
| | | |
| | | // 添加指示器 |
| | | // Bonder |
| | |
| | | |
| | | |
| | | // Robot |
| | | m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 620, 294, 48, RGB(22, 22, 22), |
| | | m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM1, 190, 294, 48, RGB(22, 22, 22), |
| | | RGB(255, 127, 39), RGB(0, 176, 80)); |
| | | m_pGraph->SetBoxText(INDICATE_ROBOT_ARM1, "5", "Robot"); |
| | | m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 673, 294, 48, RGB(22, 22, 22), |
| | | m_pGraph->AddIndicateBox(INDICATE_ROBOT_ARM2, 243, 294, 48, RGB(22, 22, 22), |
| | | RGB(255, 127, 39), RGB(0, 176, 80)); |
| | | m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "6", "Robot"); |
| | | |
| | |
| | | m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "13", "Measurement"); |
| | | |
| | | |
| | | // 调整初始窗口位置 |
| | | CRect rcWnd; |
| | | GetWindowRect(&rcWnd); |
| | | int width = GetSystemMetrics(SM_CXSCREEN); |
| | | int height = GetSystemMetrics(SM_CYSCREEN); |
| | | MoveWindow((width - rcWnd.Width()) / 2, 0, rcWnd.Width(), rcWnd.Height(), TRUE); |
| | | |
| | | |
| | | // model init |
| | | theApp.m_model.init(); |
| | | |
| | | |
| | | UpdateLogBtn(); |
| | | Resize(); |
| | | return TRUE; // 除非将焦点设置到控件,否则返回 TRUE |
| | | } |
| | | |
| | |
| | | return static_cast<HCURSOR>(m_hIcon); |
| | | } |
| | | |
| | | void CServoDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) |
| | | { |
| | | ASSERT(pPopupMenu != NULL); |
| | | |
| | | CCmdUI state; |
| | | state.m_pMenu = pPopupMenu; |
| | | ASSERT(state.m_pOther == NULL); |
| | | ASSERT(state.m_pParentMenu == NULL); |
| | | |
| | | HMENU hParentMenu; |
| | | if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu) |
| | | state.m_pParentMenu = pPopupMenu; |
| | | else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL) |
| | | { |
| | | CWnd* pParent = this; |
| | | if (pParent != NULL && |
| | | (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL) |
| | | { |
| | | int nIndexMax = ::GetMenuItemCount(hParentMenu); |
| | | for (int nIndex = 0; nIndex < nIndexMax; nIndex++) |
| | | { |
| | | if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu) |
| | | { |
| | | state.m_pParentMenu = CMenu::FromHandle(hParentMenu); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | state.m_nIndexMax = pPopupMenu->GetMenuItemCount(); |
| | | for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; |
| | | state.m_nIndex++) |
| | | { |
| | | state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex); |
| | | if (state.m_nID == 0) |
| | | continue; |
| | | |
| | | ASSERT(state.m_pOther == NULL); |
| | | ASSERT(state.m_pMenu != NULL); |
| | | if (state.m_nID == (UINT)-1) |
| | | { |
| | | state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex); |
| | | if (state.m_pSubMenu == NULL || |
| | | (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 || |
| | | state.m_nID == (UINT)-1) |
| | | { |
| | | continue; |
| | | } |
| | | state.DoUpdate(this, TRUE); |
| | | } |
| | | else |
| | | { |
| | | state.m_pSubMenu = NULL; |
| | | state.DoUpdate(this, FALSE); |
| | | } |
| | | |
| | | UINT nCount = pPopupMenu->GetMenuItemCount(); |
| | | if (nCount < state.m_nIndexMax) |
| | | { |
| | | state.m_nIndex -= (state.m_nIndexMax - nCount); |
| | | while (state.m_nIndex < nCount && |
| | | pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID) |
| | | { |
| | | state.m_nIndex++; |
| | | } |
| | | } |
| | | state.m_nIndexMax = nCount; |
| | | } |
| | | } |
| | | |
| | | void CServoDlg::OnMenuFileSettings() |
| | | { |
| | | |
| | | } |
| | | |
| | | void CServoDlg::OnUpdateMenuFileSettings(CCmdUI* pCmdUI) |
| | | { |
| | | pCmdUI->Enable(TRUE); |
| | | } |
| | | |
| | | void CServoDlg::OnMenuFileSecsTest() |
| | | { |
| | | CSecsTestDlg dlg; |
| | | dlg.DoModal(); |
| | | } |
| | | |
| | | void CServoDlg::OnUpdateMenuFileSecsTest(CCmdUI* pCmdUI) |
| | | { |
| | | pCmdUI->Enable(TRUE); |
| | | } |
| | | |
| | | void CServoDlg::OnMenuWndLog() |
| | | { |
| | | OnBnClickedButtonLog(); |
| | | } |
| | | |
| | | void CServoDlg::OnUpdateMenuWndLog(CCmdUI* pCmdUI) |
| | | { |
| | | pCmdUI->SetCheck(m_bShowLogWnd); |
| | | } |
| | | |
| | | void CServoDlg::OnMenuFileExit() |
| | | { |
| | | PostMessage(WM_CLOSE); |
| | | } |
| | | |
| | | void CServoDlg::OnUpdateMenuFileExit(CCmdUI* pCmdUI) |
| | | { |
| | | pCmdUI->Enable(TRUE); |
| | | } |
| | | |
| | | void CServoDlg::OnMenuHelpAbout() |
| | | { |
| | | CAboutDlg dlgAbout; |
| | | dlgAbout.DoModal(); |
| | | } |
| | | |
| | | void CServoDlg::OnBnClickedOk() |
| | | { |
| | |
| | | { |
| | | CDialogEx::OnDestroy(); |
| | | |
| | | if (m_pLogDlg != nullptr) { |
| | | m_pLogDlg->DestroyWindow(); |
| | | delete m_pLogDlg; |
| | | m_pLogDlg = nullptr; |
| | | } |
| | | |
| | | if (m_hbrBkgnd != nullptr) { |
| | | ::DeleteObject(m_hbrBkgnd); |
| | | } |
| | |
| | | void CServoDlg::OnBnClickedButtonLog() |
| | | { |
| | | m_bShowLogWnd = !m_bShowLogWnd; |
| | | m_btnLog.SetFrameColor(BS_NORMAL, BTN_JOG_FRAME_NORMAL); |
| | | m_btnLog.SetFrameColor(BS_HOVER, BTN_JOG_FRAME_HOVER); |
| | | m_btnLog.SetFrameColor(BS_PRESS, BTN_JOG_FRAME_PRESS); |
| | | m_btnLog.SetBkgndColor(BS_NORMAL, BTN_JOG_BKGND_NORMAL); |
| | | m_btnLog.SetBkgndColor(BS_HOVER, BTN_JOG_BKGND_HOVER); |
| | | m_btnLog.SetBkgndColor(BS_PRESS, BTN_JOG_BKGND_PRESS); |
| | | if (m_pLogDlg == nullptr) { |
| | | m_pLogDlg = new CLogDlg(); |
| | | m_pLogDlg->Create(IDD_DIALOG_LOG, this); |
| | | |
| | | CRect rcWnd; |
| | | GetWindowRect(&rcWnd); |
| | | m_pLogDlg->MoveWindow(rcWnd.left, rcWnd.bottom - 8, rcWnd.Width(), 200); |
| | | } |
| | | ASSERT(m_pLogDlg); |
| | | m_pLogDlg->ShowWindow(m_bShowLogWnd ? SW_SHOW : SW_HIDE); |
| | | |
| | | UpdateLogBtn(); |
| | | } |
| | | |
| | | void CServoDlg::UpdateLogBtn() |
| | | { |
| | | m_btnLog.SetFrameColor(BS_NORMAL, BTN_LOG_FRAME_NORMAL); |
| | | m_btnLog.SetFrameColor(BS_HOVER, BTN_LOG_FRAME_HOVER); |
| | | m_btnLog.SetFrameColor(BS_PRESS, BTN_LOG_FRAME_PRESS); |
| | | m_btnLog.SetBkgndColor(BS_NORMAL, m_bShowLogWnd ? BTN_LOG_BKGND_PRESS : BTN_LOG_BKGND_NORMAL); |
| | | m_btnLog.SetBkgndColor(BS_HOVER, BTN_LOG_BKGND_HOVER); |
| | | m_btnLog.SetBkgndColor(BS_PRESS, BTN_LOG_BKGND_PRESS); |
| | | m_btnLog.Invalidate(); |
| | | } |
| | | |
| | | void CServoDlg::UpdateRobotPosition(float percentage) |
| | | { |
| | | // 限制百分比范围在 [0, 1] 之间 |
| | | if (percentage < 0.0f) percentage = 0.0f; |
| | | if (percentage > 1.0f) percentage = 1.0f; |
| | | |
| | | // 根据百分比计算目标 X 坐标 |
| | | int startX = m_pGraph->GetImage(IMAGE_ROBOT)->x; |
| | | int endX = static_cast<int>(170 + percentage * (700 - 170)); |
| | | |
| | | int arm1Offset = 20; // 从图片到ARM1的偏移 |
| | | int arm2Offset = 73; // 从图片到ARM2的偏移 |
| | | |
| | | // 计算移动所需的时间 |
| | | int distance = abs(endX - startX); |
| | | int duration = static_cast<int>((distance / 100.0) * 1000); |
| | | |
| | | auto startTime = std::chrono::steady_clock::now(); |
| | | auto endTime = startTime + std::chrono::milliseconds(duration); |
| | | |
| | | // 开始移动,设置标记 |
| | | m_bIsRobotMoving = TRUE; |
| | | |
| | | // 开始平滑移动 |
| | | while (std::chrono::steady_clock::now() < endTime) { |
| | | auto currentTime = std::chrono::steady_clock::now(); |
| | | float progress = std::chrono::duration<float, std::milli>(currentTime - startTime).count() / duration; |
| | | progress = min(progress, 1.0f); |
| | | |
| | | // 根据进度计算当前位置 |
| | | int currentX = static_cast<int>(startX + progress * (endX - startX)); |
| | | m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, currentX, 270); |
| | | m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, currentX + arm1Offset, 294); |
| | | m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, currentX + arm2Offset, 294); |
| | | |
| | | // 刷新界面 |
| | | Invalidate(); |
| | | UpdateWindow(); |
| | | |
| | | // 控制帧率约为 60 FPS |
| | | std::this_thread::sleep_for(std::chrono::milliseconds(16)); |
| | | } |
| | | |
| | | // 确保最后位置精确到目标位置 |
| | | m_pGraph->UpdateImageCoordinates(IMAGE_ROBOT, endX, 270); |
| | | m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, endX + arm1Offset, 294); |
| | | m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, endX + arm2Offset, 294); |
| | | |
| | | // 界面重绘 |
| | | Invalidate(); |
| | | |
| | | // 动画结束,设置标记 |
| | | m_bIsRobotMoving = FALSE; |
| | | } |
| | | |
| | | void CServoDlg::RotateRobot(float angleInDegrees) |
| | | { |
| | | // 将角度转换为弧度 |
| | | float angleInRadians = static_cast<float>(std::acos(-1)) / 180.0f * angleInDegrees; |
| | | |
| | | // 获取机器人图片的当前坐标和中心 |
| | | auto* pImage = m_pGraph->GetImage(IMAGE_ROBOT); |
| | | if (!pImage) return; |
| | | |
| | | // 更新 Rotate 图片的角度,确保角度保持在 [0, 360) 范围内 |
| | | m_pGraph->UpdateImageAngle(IMAGE_ROBOT, static_cast<float>(fmod(pImage->angle + angleInDegrees + 360, 360))); |
| | | |
| | | int cx = pImage->x + pImage->bmWidth / 2; // 图片中心 X |
| | | int cy = pImage->y + pImage->bmHeight / 2; // 图片中心 Y |
| | | |
| | | // 旋转指示框的坐标 |
| | | auto* pRobot1 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM1); |
| | | auto* pRobot2 = m_pGraph->GetIndicateBox(INDICATE_ROBOT_ARM2); |
| | | |
| | | if (pRobot1 && pRobot2) { |
| | | int newArmX1 = pImage->x + 20; |
| | | int newArmY1 = 294; |
| | | |
| | | int newArmX2 = pImage->x + 73; |
| | | int newArmY2 = 294; |
| | | |
| | | if (angleInDegrees != 0.0f) { |
| | | // 计算指示框1的新坐标 |
| | | newArmX1 = static_cast<int>(cx + (pRobot1->x - cx) * cos(angleInRadians) - (pRobot1->y - cy) * sin(angleInRadians)); |
| | | newArmY1 = static_cast<int>(cy + (pRobot1->x - cx) * sin(angleInRadians) + (pRobot1->y - cy) * cos(angleInRadians)); |
| | | |
| | | // 计算指示框2的新坐标 |
| | | newArmX2 = static_cast<int>(cx + (pRobot2->x - cx) * cos(angleInRadians) - (pRobot2->y - cy) * sin(angleInRadians)); |
| | | newArmY2 = static_cast<int>(cy + (pRobot2->x - cx) * sin(angleInRadians) + (pRobot2->y - cy) * cos(angleInRadians)); |
| | | } |
| | | |
| | | // 更新指示框的位置 |
| | | m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM1, newArmX1, newArmY1); |
| | | m_pGraph->UpdateIndicateBoxCoordinates(INDICATE_ROBOT_ARM2, newArmX2, newArmY2); |
| | | } |
| | | |
| | | // 强制重绘界面 |
| | | Invalidate(); |
| | | } |
| | | |
| | | void CServoDlg::OnSize(UINT nType, int cx, int cy) |
| | | { |
| | | CDialogEx::OnSize(nType, cx, cy); |
| | | if (GetDlgItem(IDC_SERVO_GRAPH1) == nullptr) return; |
| | | Resize(); |
| | | } |
| | | |
| | | void CServoDlg::Resize() |
| | | { |
| | | CRect rcClient, rcItem; |
| | | CWnd* pItem = nullptr; |
| | | int x, y; |
| | | |
| | | x = 0; |
| | | y = 0; |
| | | pItem = GetDlgItem(IDC_SERVO_GRAPH1); |
| | | pItem->GetClientRect(&rcItem); |
| | | pItem->MoveWindow(x, y, rcItem.Width(), rcItem.Height()); |
| | | y += rcItem.Height(); |
| | | y += 8; |
| | | |
| | | x = 8; |
| | | pItem = GetDlgItem(IDC_BUTTON_LOG); |
| | | pItem->GetClientRect(&rcItem); |
| | | pItem->MoveWindow(x, y, rcItem.Width(), rcItem.Height()); |
| | | } |
| | | |
| | | void CServoDlg::OnClose() |
| | | { |
| | | // TODO: 在此添加消息处理程序代码和/或调用默认值 |
| | | |
| | | CDialogEx::OnClose(); |
| | | } |
| | | |
| | | LRESULT CServoDlg::OnLogDlgHide(WPARAM wParam, LPARAM lParam) |
| | | { |
| | | m_bShowLogWnd = FALSE; |
| | | UpdateLogBtn(); |
| | | LOGE("OnLogDlgHide"); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void CServoDlg::OnMoving(UINT fwSide, LPRECT pRect) |
| | | { |
| | | CDialogEx::OnMoving(fwSide, pRect); |
| | | } |
| | | |
| | | void CServoDlg::OnMove(int x, int y) |
| | | { |
| | | if (m_pLogDlg != nullptr && !m_pLogDlg->IsZoomed()) { |
| | | CRect rcWnd; |
| | | GetWindowRect(&rcWnd); |
| | | m_pLogDlg->MoveWindow(rcWnd.left, rcWnd.bottom - 8, rcWnd.Width(), 200); |
| | | } |
| | | |
| | | CDialogEx::OnMove(x, y); |
| | | } |
| | | |
| | | |
| | | BOOL CServoDlg::OnEraseBkgnd(CDC* pDC) |
| | | { |
| | | // TODO: 在此添加消息处理程序代码和/或调用默认值 |
| | | if (m_bIsRobotMoving) { |
| | | // 禁止刷新背景,避免闪烁 |
| | | return TRUE; |
| | | } |
| | | |
| | | return CDialogEx::OnEraseBkgnd(pDC); |
| | | } |