// CRobotCmdTestDlg.cpp: 实现文件 // #include "stdafx.h" #include "Servo.h" #include "afxdialogex.h" #include "CRobotCmdTestDlg.h" // 日志颜色宏定义 #define LOG_COLOR_NORMAL RGB(0, 0, 0) // 普通:黑色 #define LOG_COLOR_SUCCESS RGB(0, 128, 0) // 成功:绿色 #define LOG_COLOR_ERROR RGB(255, 0, 0) // 错误:红色 #define LOG_COLOR_WARNING RGB(255, 165, 0) // 警告:橙色 #define LOG_COLOR_TIME RGB(0, 0, 255) // 时间戳:蓝色 // 快捷命令映射表 std::map g_mapQuickCmd = { {_T("SendHome"), {false, false, false, false, false}}, {_T("SendTransfer"), {true, true, true, true, true }}, {_T("SendMoveToGet"), {true, true, true, false, false}}, {_T("SendMoveToPut"), {true, false, false, true, true }}, {_T("SendGet"), {true, true, true, false, false}}, {_T("SendPut"), {true, false, false, true, true }}, {_T("SendExchange"), {true, true, true, true, true }}, {_T("SendCommandClear"), {false, false, false, false, false}}, {_T("SendBatchGet"), {true, true, true, false, false}}, {_T("SendBatchPut"), {true, false, false, true, true }}, {_T("SendMoveToGetAndHome"), {true, true, true, false, false}}, {_T("SendMoveToPutAndHome"), {true, false, false, true, true }}, {_T("SendTransferAndHome"), {true, true, true, true, true }}, {_T("SendGetAndPut"), {true, true, true, true, true }}, {_T("SendPutAndHome"), {true, false, false, true, true }} }; std::map g_mapDeviceSlotCount = { {_T("Port1"), 8}, {_T("Port2"), 8}, {_T("Port3"), 8}, {_T("Port4"), 8}, {_T("RB1"), 1}, {_T("RB2"), 1}, {_T("AL"), 1}, {_T("FLIP"), 1}, {_T("Bonder1-上产品"), 1}, {_T("Bonder1-下产品"), 1}, {_T("Bonder2-上产品"), 1}, {_T("Bonder2-下产品"), 1}, {_T("VacBakeA腔"), 1}, {_T("VacBakeB腔"), 1}, {_T("BakecCoolingA烘烤"), 1}, {_T("BakecCoolingA冷却"), 1}, {_T("BakecCoolingB烘烤"), 1}, {_T("BakecCoolingB冷却"), 1}, {_T("Measurement"), 1} }; // CRobotCmdTestDlg 对话框 IMPLEMENT_DYNAMIC(CRobotCmdTestDlg, CDialogEx) CRobotCmdTestDlg::CRobotCmdTestDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_DIALOG_ROBOT_CMD_TEST, pParent) { m_pEFEM = nullptr; } CRobotCmdTestDlg::~CRobotCmdTestDlg() { } void CRobotCmdTestDlg::SetEFEM(SERVO::CEFEM* pEFEM) { m_pEFEM = pEFEM; } void CRobotCmdTestDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_COMBO_CMD_TYPE, m_comboCmdType); DDX_Control(pDX, IDC_COMBO_ARM_NO, m_comboArmNo); DDX_Control(pDX, IDC_COMBO_GET_POS, m_comboGetPos); DDX_Control(pDX, IDC_COMBO_GET_SLOT, m_comboGetSlot); DDX_Control(pDX, IDC_COMBO_PUT_POS, m_comboPutPos); DDX_Control(pDX, IDC_COMBO_PUT_SLOT, m_comboPutSlot); DDX_Control(pDX, IDC_RICHEDIT_LOG, m_editLog); } BEGIN_MESSAGE_MAP(CRobotCmdTestDlg, CDialogEx) ON_BN_CLICKED(IDC_BUTTON_EXECUTE, &CRobotCmdTestDlg::OnBnClickedButtonExecute) ON_CBN_SELCHANGE(IDC_COMBO_CMD_TYPE, &CRobotCmdTestDlg::OnSelchangeComboCmdType) ON_CBN_SELCHANGE(IDC_COMBO_GET_POS, &CRobotCmdTestDlg::OnSelchangeComboGetPos) ON_CBN_SELCHANGE(IDC_COMBO_PUT_POS, &CRobotCmdTestDlg::OnSelchangeComboPutPos) END_MESSAGE_MAP() // CRobotCmdTestDlg 消息处理程序 BOOL CRobotCmdTestDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // TODO: 在此添加额外的初始化 // 初始化命令组合列表 for (const auto& pair : g_mapQuickCmd) { m_comboCmdType.AddString(pair.first); } m_comboCmdType.SetCurSel(0); // 初始化 Arm No(1:Arm#1,2:Arm#2) m_comboArmNo.AddString(_T("1")); m_comboArmNo.AddString(_T("2")); m_comboArmNo.SetCurSel(0); // 示例设备名称列表 CStringList eqNameList; eqNameList.AddTail(_T("Port1")); eqNameList.AddTail(_T("Port2")); eqNameList.AddTail(_T("Port3")); eqNameList.AddTail(_T("Port4")); eqNameList.AddTail(_T("RB1")); eqNameList.AddTail(_T("RB2")); eqNameList.AddTail(_T("AL")); eqNameList.AddTail(_T("FLIP")); eqNameList.AddTail(_T("Bonder1-上产品")); eqNameList.AddTail(_T("Bonder1-下产品")); eqNameList.AddTail(_T("Bonder2-上产品")); eqNameList.AddTail(_T("Bonder2-下产品")); eqNameList.AddTail(_T("VacBakeA腔")); eqNameList.AddTail(_T("VacBakeB腔")); eqNameList.AddTail(_T("BakecCoolingA烘烤")); eqNameList.AddTail(_T("BakecCoolingA冷却")); eqNameList.AddTail(_T("BakecCoolingB烘烤")); eqNameList.AddTail(_T("BakecCoolingB冷却")); eqNameList.AddTail(_T("Measurement")); for (POSITION pos = eqNameList.GetHeadPosition(); pos != NULL;) { CString item = eqNameList.GetNext(pos); m_comboGetPos.AddString(item); m_comboPutPos.AddString(item); } m_comboGetPos.SetCurSel(0); m_comboPutPos.SetCurSel(0); // 初始化 Slot m_comboGetPos.SetCurSel(0); m_comboPutPos.SetCurSel(0); UpdateSlotList(&m_comboGetPos, &m_comboGetSlot); UpdateSlotList(&m_comboPutPos, &m_comboPutSlot); // 初始化命令输入控件 if (!g_mapQuickCmd.empty()) { UpdateCommandInputUI(g_mapQuickCmd.begin()->first); } // 快捷命令执行映射表 m_mapCmdExec = { {_T("SendHome"), [this](int seq, int, int, int, int, int, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendHome(seq, onWritedBlock); }}, {_T("SendTransfer"), [this](int seq, int armNo, int getPos, int putPos, int getSlot, int putSlot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendTransfer(seq, armNo, getPos, putPos, getSlot, putSlot, onWritedBlock); }}, {_T("SendMoveToGet"), [this](int seq, int armNo, int pos, int, int slot, int, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendMoveToGet(seq, armNo, pos, slot, onWritedBlock); }}, {_T("SendMoveToPut"), [this](int seq, int armNo, int, int pos, int, int slot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendMoveToPut(seq, armNo, pos, slot, onWritedBlock); }}, {_T("SendGet"), [this](int seq, int armNo, int pos, int, int slot, int, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendGet(seq, armNo, pos, slot, onWritedBlock); }}, {_T("SendPut"), [this](int seq, int armNo, int, int pos, int, int slot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendPut(seq, armNo, pos, slot, onWritedBlock); }}, {_T("SendExchange"), [this](int seq, int armNo, int pos, int, int getSlot, int putSlot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendExchange(seq, armNo, pos, getSlot, putSlot, onWritedBlock); }}, {_T("SendCommandClear"), [this](int seq, int, int, int, int, int, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendCommandClear(seq, onWritedBlock); }}, {_T("SendBatchGet"), [this](int seq, int armNo, int pos, int, int slot, int, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendBatchGet(seq, pos, slot, onWritedBlock); }}, {_T("SendBatchPut"), [this](int seq, int armNo, int, int pos, int, int slot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendBatchPut(seq, pos, slot, onWritedBlock); }}, {_T("SendMoveToGetAndHome"), [this](int seq, int armNo, int pos, int, int slot, int, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendMoveToGetAndHome(seq, armNo, pos, slot, onWritedBlock); }}, {_T("SendMoveToPutAndHome"), [this](int seq, int armNo, int, int pos, int, int slot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendMoveToPutAndHome(seq, armNo, pos, slot, onWritedBlock); }}, {_T("SendTransferAndHome"), [this](int seq, int armNo, int getPos, int putPos, int getSlot, int putSlot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendTransferAndHome(seq, armNo, getPos, putPos, getSlot, putSlot, onWritedBlock); }}, {_T("SendGetAndPut"), [this](int seq, int armNo, int getPos, int putPos, int getSlot, int putSlot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendGetAndPut(seq, armNo, getPos, putPos, getSlot, putSlot, onWritedBlock); }}, {_T("SendPutAndHome"), [this](int seq, int armNo, int, int pos, int, int slot, SERVO::ONWRITED onWritedBlock) { return m_pEFEM->robotSendPutAndHome(seq, armNo, pos, slot, onWritedBlock); }} }; // 初始化日志框 AppendLogLineRichStyled(_T("准备就绪..."), LOG_COLOR_SUCCESS); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CRobotCmdTestDlg::UpdateCommandInputUI(const CString& cmdName) { auto it = g_mapQuickCmd.find(cmdName); if (it == g_mapQuickCmd.end()) { AppendLogLineRichStyled(_T("未知的命令类型"), LOG_COLOR_ERROR); return; } const QuickCmdFieldMask& mask = it->second; m_comboArmNo.SetCurSel(0); m_comboArmNo.EnableWindow(mask.useArm); m_comboGetPos.SetCurSel(0); m_comboGetPos.EnableWindow(mask.useGetPos); m_comboGetSlot.SetCurSel(0); m_comboGetSlot.EnableWindow(mask.useGetSlot); m_comboPutPos.SetCurSel(0); m_comboPutPos.EnableWindow(mask.usePutPos); m_comboPutSlot.SetCurSel(0); m_comboPutSlot.EnableWindow(mask.usePutSlot); } void CRobotCmdTestDlg::AppendLogLineBatchBegin() { m_editLog.SetRedraw(FALSE); m_editLog.SetEventMask(0); // 防止触发不必要的通知 } void CRobotCmdTestDlg::AppendLogLineBatchEnd() { m_editLog.SetRedraw(TRUE); m_editLog.Invalidate(); // 强制重绘 m_editLog.SetEventMask(ENM_CHANGE | ENM_SELCHANGE); } void CRobotCmdTestDlg::TrimRichEditLineLimit(int maxLines) { int lineCount = m_editLog.GetLineCount(); if (lineCount <= maxLines) { return; } // 获取多余行的字符数范围 int charIndex = m_editLog.LineIndex(maxLines); m_editLog.SetSel(0, charIndex); // 选中多余内容 m_editLog.ReplaceSel(_T("")); // 删除 } void CRobotCmdTestDlg::AppendLogLineRichStyled(const CString& content, COLORREF color /*= RGB(0, 0, 0)*/) { // 时间戳 CString timestamp; CTime now = CTime::GetCurrentTime(); timestamp.Format(_T("[%02d:%02d:%02d] "), now.GetHour(), now.GetMinute(), now.GetSecond()); // 插入点移到最后(也可以设为 0 表示顶部) m_editLog.SetSel(-1, -1); // 插入时间(蓝色) CHARFORMAT2 cfTime = {}; cfTime.cbSize = sizeof(cfTime); cfTime.dwMask = CFM_COLOR; cfTime.crTextColor = LOG_COLOR_TIME; m_editLog.SetSelectionCharFormat(cfTime); m_editLog.ReplaceSel(timestamp); // 插入日志正文(传入颜色) CHARFORMAT2 cfMsg = {}; cfMsg.cbSize = sizeof(cfMsg); cfMsg.dwMask = CFM_COLOR; cfMsg.crTextColor = color; m_editLog.SetSelectionCharFormat(cfMsg); m_editLog.ReplaceSel(content + _T("\r\n")); // 限制最大行数 TrimRichEditLineLimit(100); } void CRobotCmdTestDlg::HighlightAllMatches(const CString& strSearch, COLORREF clrHighlight) { if (strSearch.IsEmpty()) { return; } long nStart = 0; long nEnd = m_editLog.GetTextLength(); FINDTEXTEX ft = { 0 }; ft.chrg.cpMin = 0; ft.chrg.cpMax = nEnd; ft.lpstrText = strSearch.GetString(); // 高亮前不清除全文颜色,避免历史多色混淆 while (m_editLog.FindText(FR_DOWN, &ft) != -1) { m_editLog.SetSel(ft.chrgText.cpMin, ft.chrgText.cpMax); CHARFORMAT2 cf = {}; cf.cbSize = sizeof(cf); cf.dwMask = CFM_COLOR; cf.crTextColor = clrHighlight; m_editLog.SetSelectionCharFormat(cf); // 下次搜索从后面开始 ft.chrg.cpMin = ft.chrgText.cpMax; } m_editLog.SetSel(-1, 0); } void CRobotCmdTestDlg::UpdateSlotList(CComboBox* pComboDevice, CComboBox* pComboSlot) { if (pComboDevice == nullptr || pComboSlot == nullptr) { return; } int nSelIndex = pComboDevice->GetCurSel(); if (nSelIndex < 0) { return; } CString strDeviceName; pComboDevice->GetLBText(nSelIndex, strDeviceName); int nMaxSlot = 1; // 默认槽位数 auto it = g_mapDeviceSlotCount.find(strDeviceName); if (it != g_mapDeviceSlotCount.end()) { nMaxSlot = it->second; } else { CString strLog; strLog.Format(_T("设备 %s 未定义槽位数,使用默认值 1"), strDeviceName.GetString()); AppendLogLineRichStyled(strLog, LOG_COLOR_WARNING); } // 清空并添加槽号 pComboSlot->ResetContent(); for (int i = 1; i <= nMaxSlot; ++i) { CString str; str.Format(_T("%d"), i); pComboSlot->AddString(str); } pComboSlot->SetCurSel(0); pComboSlot->EnableWindow(1 != nMaxSlot); } void CRobotCmdTestDlg::UpdateArmList(CComboBox* pComboDevice, CComboBox* pComboArm) { if (pComboDevice == nullptr || pComboArm == nullptr) { return; } int nSelIndex = pComboDevice->GetCurSel(); if (nSelIndex < 0) { return; } CString strDeviceName; pComboDevice->GetLBText(nSelIndex, strDeviceName); if (IsDualArmSupported(strDeviceName)) { pComboArm->EnableWindow(TRUE); } else { pComboArm->EnableWindow(FALSE); } pComboArm->SetCurSel(0); } bool CRobotCmdTestDlg::IsDualArmSupported(const CString& strDeviceName) { // 注意匹配规则是否区分“-上产品”、“-下产品”等后缀 if (strDeviceName.Find(_T("FLIP")) >= 0) return true; if (strDeviceName.Find(_T("Bonder1")) >= 0) return true; if (strDeviceName.Find(_T("Bonder2")) >= 0) return true; return false; } void CRobotCmdTestDlg::OnBnClickedButtonExecute() { // TODO: 在此添加控件通知处理程序代码 int sel = m_comboCmdType.GetCurSel(); if (sel == CB_ERR) { AppendLogLineRichStyled(_T("未选择命令类型"), LOG_COLOR_ERROR); return; } CString cmdName; m_comboCmdType.GetLBText(sel, cmdName); // 参数收集 int armNo = m_comboArmNo.GetCurSel() + 1; int getPos = m_comboGetPos.GetCurSel() + 1; int getSlot = m_comboGetSlot.GetCurSel() + 1; int putPos = m_comboPutPos.GetCurSel() + 1; int putSlot = m_comboPutSlot.GetCurSel() + 1; // 查找函数并执行 auto it = m_mapCmdExec.find(cmdName); if (it != m_mapCmdExec.end() && nullptr != m_pEFEM) { int ret = it->second(1, armNo, getPos, putPos, getSlot, putSlot, [&](int code) -> int { if (code == WOK) { AppendLogLineRichStyled(_T("已收到Robot回应!"), LOG_COLOR_SUCCESS); } else { AppendLogLineRichStyled(_T("未收到Robot回应!"), LOG_COLOR_ERROR); } return 0; }); CString log; if (ret == 0) { log.Format(_T("执行命令 %s 成功"), cmdName.GetString()); AppendLogLineRichStyled(log, LOG_COLOR_SUCCESS); } else { log.Format(_T("执行命令 %s 参数错误"), cmdName.GetString()); AppendLogLineRichStyled(log, LOG_COLOR_ERROR); } } else { CString log; log.Format(_T("命令 %s 不存在或 EFEM 未初始化"), cmdName.GetString()); AppendLogLineRichStyled(log, LOG_COLOR_ERROR); } } void CRobotCmdTestDlg::OnSelchangeComboCmdType() { int nSel = m_comboCmdType.GetCurSel(); if (nSel < 0) { return; } CString str; m_comboCmdType.GetLBText(nSel, str); UpdateCommandInputUI(str); str.Format(_T("切换当前命令类型:%s"), str); AppendLogLineRichStyled(str, LOG_COLOR_NORMAL); } void CRobotCmdTestDlg::OnSelchangeComboGetPos() { // TODO: 在此添加控件通知处理程序代码 UpdateSlotList(&m_comboGetPos, &m_comboGetSlot); UpdateArmList(&m_comboGetPos, &m_comboArmNo); } void CRobotCmdTestDlg::OnSelchangeComboPutPos() { // TODO: 在此添加控件通知处理程序代码 UpdateSlotList(&m_comboPutPos, &m_comboPutSlot); UpdateArmList(&m_comboPutPos, &m_comboArmNo); }