| | |
| | | } |
| | | } |
| | | |
| | | { |
| | | // master recipe list report |
| | | CEqReadStep* pStep = new CEqReadStep(0x6955, 255 * 2, |
| | | [&](void* pFrom, int code, const char* pszData, size_t size) -> int { |
| | | /*CEqReadStep* pTmpStep = dynamic_cast<CEqReadStep*>((CEqReadStep*)pFrom);*/ |
| | | CEqReadStep* pTmpStep = (CEqReadStep*)pFrom; |
| | | short ret = MRLRC_OK; |
| | | if (code == ROK && pszData != nullptr && size > 0) { |
| | | // 此处解释配方数据 |
| | | ret = decodeRecipeListReport(pszData, size); |
| | | } |
| | | pTmpStep->setReturnCode(ret); |
| | | return -1; |
| | | }); |
| | | pStep->setName(STEP_EQ_MASTER_RECIPE_LIST); |
| | | pStep->setWriteSignalDev(0x4b); |
| | | pStep->setReturnDev(0x91d); |
| | | if (addStep(STEP_ID_MASTER_RECIPE_LIST_REPORT, pStep) != 0) { |
| | | delete pStep; |
| | | } |
| | | } |
| | | |
| | | // 使用CEqReadStep替换CEqJobEventStep |
| | | { |
| | | // Received Job Report Upstream #1~9 |
| | |
| | | m_nProcessResonCode = 0; |
| | | m_nLastGlassFlag = 0; |
| | | m_nFirstGlassFlag = 0; |
| | | m_nQTime[3] = {0}; |
| | | m_nQTime[3] = { 0 }; |
| | | m_nQTimeOverFlag = 0; |
| | | m_nMasterRecipe = 0; |
| | | m_nRecipeIds[DEVICE_COUNT] = { 0 }; |
| | | m_nMode = 0; |
| | | m_nSlotUnitSelectFlag = 0; |
| | | m_nSourcePortNo = 0; |
| | | m_nSourceSlotNo = 0; |
| | | m_nTargetPortNo = 0; |
| | | m_nTargetSlotNo = 0; |
| | | m_nProductJudge = 0; |
| | | m_pRawData = nullptr; |
| | | m_pOwner = nullptr; |
| | | if (ENABLE_JOBDATAS_RAWDATA) { |
| | |
| | | m_nQTime[2] = pScr->m_nQTime[2]; |
| | | m_nQTimeOverFlag = pScr->m_nQTimeOverFlag; |
| | | m_nMasterRecipe = pScr->m_nMasterRecipe; |
| | | m_strProductRecipeId = pScr->m_strProductRecipeId; |
| | | m_strPCode = pScr->m_strPCode; |
| | | m_strUseType = pScr->m_strUseType; |
| | | memcpy(m_nRecipeIds, pScr->m_nRecipeIds, sizeof(m_nRecipeIds)); |
| | | m_strPanelMeasure = pScr->m_strPanelMeasure; |
| | | m_nMode = pScr->m_nMode; |
| | | m_nSlotUnitSelectFlag = pScr->m_nSlotUnitSelectFlag; |
| | |
| | | m_nSourceSlotNo = pScr->m_nSourceSlotNo; |
| | | m_nTargetPortNo = pScr->m_nTargetPortNo; |
| | | m_nTargetSlotNo = pScr->m_nTargetSlotNo; |
| | | m_nProductJudge = pScr->m_nProductJudge; |
| | | m_pOwner = pScr->m_pOwner; |
| | | } |
| | | |
| | |
| | | m_nQTime[2] = pScr->m_nQTime[2]; |
| | | m_nQTimeOverFlag = pScr->m_nQTimeOverFlag; |
| | | m_nMasterRecipe = pScr->m_nMasterRecipe; |
| | | m_strProductRecipeId = pScr->m_strProductRecipeId; |
| | | m_strPCode = pScr->m_strPCode; |
| | | m_strUseType = pScr->m_strUseType; |
| | | memcpy(m_nRecipeIds, pScr->m_nRecipeIds, sizeof(m_nRecipeIds)); |
| | | m_strPanelMeasure = pScr->m_strPanelMeasure; |
| | | m_nMode = pScr->m_nMode; |
| | | m_nSlotUnitSelectFlag = pScr->m_nSlotUnitSelectFlag; |
| | |
| | | m_nSourceSlotNo = pScr->m_nSourceSlotNo; |
| | | m_nTargetPortNo = pScr->m_nTargetPortNo; |
| | | m_nTargetSlotNo = pScr->m_nTargetSlotNo; |
| | | m_nProductJudge = pScr->m_nProductJudge; |
| | | m_pOwner = pScr->m_pOwner; |
| | | } |
| | | |
| | |
| | | m_nMasterRecipe = recipe; |
| | | } |
| | | |
| | | std::string& CJobDataS::getProductRecipeId() |
| | | short CJobDataS::getDeviceRecipeId(int nDeviceIndex) const |
| | | { |
| | | return m_strProductRecipeId; |
| | | if (nDeviceIndex < 0 || nDeviceIndex >= DEVICE_COUNT) { |
| | | return 0; |
| | | } |
| | | |
| | | return m_nRecipeIds[nDeviceIndex]; |
| | | } |
| | | |
| | | void CJobDataS::setProductRecipeId(const char* pszId) |
| | | void CJobDataS::setDeviceRecipeId(int nDeviceIndex, short nRecipeId) |
| | | { |
| | | m_strProductRecipeId = pszId; |
| | | if (nDeviceIndex < 0 || nDeviceIndex >= DEVICE_COUNT) { |
| | | return; |
| | | } |
| | | |
| | | m_nRecipeIds[nDeviceIndex] = nRecipeId; |
| | | } |
| | | |
| | | std::string& CJobDataS::getPCode() |
| | | { |
| | | return m_strPCode; |
| | | const short* CJobDataS::getRecipeIds() const |
| | | { |
| | | return m_nRecipeIds; |
| | | } |
| | | |
| | | void CJobDataS::setPCode(const char* pszCode) |
| | | void CJobDataS::setRecipeIds(const short* pIds, int nCount) |
| | | { |
| | | m_strPCode = pszCode; |
| | | } |
| | | int nCopyCount = nCount > DEVICE_COUNT ? DEVICE_COUNT : nCount; |
| | | for (int i = 0; i < nCopyCount; ++i) { |
| | | m_nRecipeIds[i] = pIds[i]; |
| | | } |
| | | |
| | | std::string& CJobDataS::getUseType() |
| | | { |
| | | return m_strPCode; |
| | | } |
| | | |
| | | void CJobDataS::setUseType(const char* pszType) |
| | | { |
| | | m_strPCode = pszType; |
| | | // 如果 nCount < DEVICE_COUNT,可以选择补零 |
| | | for (int i = nCopyCount; i < DEVICE_COUNT; ++i) { |
| | | m_nRecipeIds[i] = 0; |
| | | } |
| | | } |
| | | |
| | | std::string& CJobDataS::getPanelMeasure() |
| | |
| | | void CJobDataS::setTargetSlotNo(int no) |
| | | { |
| | | m_nTargetSlotNo = no; |
| | | } |
| | | |
| | | short CJobDataS::getProductJudge() const |
| | | { |
| | | return m_nProductJudge; |
| | | } |
| | | |
| | | void CJobDataS::setProductJudge(short nProductJudge) |
| | | { |
| | | m_nProductJudge = nProductJudge; |
| | | } |
| | | |
| | | int CJobDataS::serialize(char* pszBuffer, int nBufferSize) |
| | |
| | | memcpy(&pszBuffer[index], &m_nMasterRecipe, sizeof(short)); |
| | | index += sizeof(short); |
| | | |
| | | strLen = min(10, (int)m_strProductRecipeId.size()); |
| | | memcpy(&pszBuffer[index], m_strProductRecipeId.c_str(), strLen); |
| | | index += 10; |
| | | |
| | | strLen = min(10, (int)m_strPCode.size()); |
| | | memcpy(&pszBuffer[index], m_strPCode.c_str(), strLen); |
| | | index += 10; |
| | | |
| | | strLen = min(10, (int)m_strUseType.size()); |
| | | memcpy(&pszBuffer[index], m_strUseType.c_str(), strLen); |
| | | index += 10; |
| | | for (int i = 0; i < DEVICE_COUNT; i++) { |
| | | memcpy(&pszBuffer[index], &m_nRecipeIds[i], sizeof(short)); |
| | | index += sizeof(short); |
| | | } |
| | | |
| | | strLen = min(80, (int)m_strPanelMeasure.size()); |
| | | memcpy(&pszBuffer[index], m_strPanelMeasure.c_str(), strLen); |
| | |
| | | index += sizeof(short); |
| | | |
| | | memcpy(&pszBuffer[index], &m_nTargetSlotNo, sizeof(short)); |
| | | index += sizeof(short); |
| | | |
| | | memcpy(&pszBuffer[index], &m_nProductJudge, sizeof(short)); |
| | | index += sizeof(short); |
| | | |
| | | return 256 * 2; |
| | |
| | | memcpy(&m_nMasterRecipe, &pszBuffer[index], sizeof(short)); |
| | | index += sizeof(short); |
| | | |
| | | CToolUnits::convertString(&pszBuffer[index], 10, m_strProductRecipeId); |
| | | index += 10; |
| | | for (int i = 0; i < DEVICE_COUNT; i++) { |
| | | memcpy(&m_nRecipeIds[i], &pszBuffer[index], sizeof(short)); |
| | | index += sizeof(short); |
| | | } |
| | | |
| | | CToolUnits::convertString(&pszBuffer[index], 10, m_strProductRecipeId); |
| | | index += 10; |
| | | |
| | | CToolUnits::convertString(&pszBuffer[index], 10, m_strProductRecipeId); |
| | | index += 10; |
| | | |
| | | CToolUnits::convertString(&pszBuffer[index], 80, m_strProductRecipeId); |
| | | CToolUnits::convertString(&pszBuffer[index], 80, m_strPanelMeasure); |
| | | index += 80; |
| | | |
| | | memcpy(&m_nMode, &pszBuffer[index], sizeof(short)); |
| | |
| | | memcpy(&m_nTargetSlotNo, &pszBuffer[index], sizeof(short)); |
| | | index += sizeof(short); |
| | | |
| | | memcpy(&m_nProductJudge, &pszBuffer[index], sizeof(short)); |
| | | index += sizeof(short); |
| | | |
| | | // 缓存原始数据 |
| | | if (m_pRawData != nullptr) { |
| | | memcpy(m_pRawData, pszBuffer, JOBDATAS_SIZE); |
| | | } |
| | | |
| | | |
| | | return JOBDATAS_SIZE; |
| | | } |
| | |
| | | attrubutes.addAttribute(new CAttribute("MasterRecipe", |
| | | std::to_string(getMasterRecipe()).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("ProductRecipeId", |
| | | getProductRecipeId().c_str(), "", weight++)); |
| | | attrubutes.addAttribute(new CAttribute("Recipe ID(EFEM)", |
| | | std::to_string(getDeviceRecipeId(0)).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("PCode", |
| | | getPCode().c_str(), "", weight++)); |
| | | attrubutes.addAttribute(new CAttribute("Recipe ID(BONDER1)", |
| | | std::to_string(getDeviceRecipeId(1)).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("UseType", |
| | | getUseType().c_str(), "", weight++)); |
| | | attrubutes.addAttribute(new CAttribute("Recipe ID(BONDER2)", |
| | | std::to_string(getDeviceRecipeId(2)).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("Recipe ID(Bake Cooling)", |
| | | std::to_string(getDeviceRecipeId(3)).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("Recipe ID(Vacuum Bake)", |
| | | std::to_string(getDeviceRecipeId(4)).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("Recipe ID(Measurement)", |
| | | std::to_string(getDeviceRecipeId(5)).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("PanelMeasure", |
| | | getPanelMeasure().c_str(), "", weight++)); |
| | |
| | | |
| | | attrubutes.addAttribute(new CAttribute("TargetSlotNo", |
| | | std::to_string(getTargetSlotNo()).c_str(), "", weight++)); |
| | | |
| | | attrubutes.addAttribute(new CAttribute("ProductJudge", |
| | | std::to_string(getProductJudge()).c_str(), "", weight++)); |
| | | } |
| | | } |
| | |
| | | #include "CAttributeVector.h" |
| | | #include "CJobDataB.h" |
| | | |
| | | |
| | | #define DEVICE_COUNT 15 |
| | | #define JOBDATAS_SIZE (256 * 2) |
| | | namespace SERVO { |
| | | class CJobDataS |
| | |
| | | void setQTimeOverFlag(int flag); |
| | | int getMasterRecipe(); |
| | | void setMasterRecipe(int recipe); |
| | | std::string& getProductRecipeId(); |
| | | void setProductRecipeId(const char* pszId); |
| | | std::string& getPCode(); |
| | | void setPCode(const char* pszCode); |
| | | std::string& getUseType(); |
| | | void setUseType(const char* pszType); |
| | | short getDeviceRecipeId(int nDeviceIndex) const; |
| | | void setDeviceRecipeId(int nDeviceIndex, short nRecipeId); |
| | | const short* getRecipeIds() const; |
| | | void setRecipeIds(const short* pIds, int nCount); |
| | | std::string& getPanelMeasure(); |
| | | void setPanelMeasure(const char* pszMeasure); |
| | | int getMode(); |
| | |
| | | void setTargetPortNo(int no); |
| | | int getTargetSlotNo(); |
| | | void setTargetSlotNo(int no); |
| | | short getProductJudge() const; |
| | | void setProductJudge(short nProductJudge); |
| | | int serialize(char* pszBuffer, int nBufferSize); |
| | | int unserialize(const char* pszBuffer, int nBufferSize); |
| | | void getAttributeVector(CAttributeVector& attrubutes, int beginWeight); |
| | |
| | | int m_nQTime[3]; |
| | | int m_nQTimeOverFlag; |
| | | int m_nMasterRecipe; |
| | | std::string m_strProductRecipeId; |
| | | std::string m_strPCode; |
| | | std::string m_strUseType; |
| | | short m_nRecipeIds[DEVICE_COUNT]; |
| | | std::string m_strPanelMeasure; |
| | | int m_nMode; |
| | | int m_nSlotUnitSelectFlag; |
| | |
| | | int m_nSourceSlotNo; |
| | | int m_nTargetPortNo; |
| | | int m_nTargetSlotNo; |
| | | short m_nProductJudge; |
| | | |
| | | private: |
| | | char* m_pRawData; |
| | |
| | | return m_nUnitNo; |
| | | } |
| | | |
| | | int CRecipeList::addRecipePacket(int totalGroup, int currentGroup, const char* pszData, size_t size) |
| | | int CRecipeList::addRecipePacket(int totalCount, int totalGroup, int currentGroup, const char* pszData, size_t size) |
| | | { |
| | | if (m_nToatlGroupCount == 0) m_nToatlGroupCount = totalGroup; |
| | | if (m_nToatlGroupCount != totalGroup) { |
| | |
| | | for (int i = 0; i < size; i += 4) { |
| | | int index = CToolUnits::toInt16(&pszData[i]); |
| | | short id = CToolUnits::toInt16(&pszData[i + 2]); |
| | | addRecipe(index, id); |
| | | if (index != 0 && id != 0) { |
| | | addRecipe(index, id); |
| | | } |
| | | } |
| | | |
| | | if (m_nCurrentGroupCount == m_nToatlGroupCount) { |
| | |
| | | } |
| | | |
| | | m_ids[index] = id; |
| | | return 0; |
| | | return (int)m_ids.size(); |
| | | } |
| | | |
| | | std::map<int, short>& CRecipeList::getIds() |
| | |
| | | |
| | | public: |
| | | int getUnitNo(); |
| | | int addRecipePacket(int totalGroup, int currentGroup, const char* pszData, size_t size); |
| | | int addRecipePacket(int totalCount,int totalGroup, int currentGroup, const char* pszData, size_t size); |
| | | int addRecipe(int index, short id); |
| | | std::map<int, short>& getIds(); |
| | | void reset(); |
| | |
| | | if (m_onSyncingStateChanged != nullptr) { |
| | | m_onSyncingStateChanged(m_nSyncStatus); |
| | | } |
| | | } |
| | | |
| | | void CRecipesManager::syncTimeout() |
| | | { |
| | | lock(); |
| | | m_nSyncStatus = SS_TIMEOUT; |
| | | m_nTimeoutCount = 0; |
| | | unlock(); |
| | | |
| | | if (m_onSyncingStateChanged != nullptr) { |
| | | m_onSyncingStateChanged(m_nSyncStatus); |
| | | } |
| | | } |
| | | |
| | | short CRecipesManager::decodeRecipeListReport(const char* pszData, size_t size) |
| | |
| | | |
| | | } |
| | | else if (reportType == RT_REQUEST_FROM_EAS) { |
| | | int nRet = pRecipeList->addRecipePacket(toatlGroupCount, currentGroupCount, pszIdsData, 250 * 2); |
| | | int nRet = pRecipeList->addRecipePacket(totalMasterRecipeCount, toatlGroupCount, currentGroupCount, pszIdsData, 250 * 2); |
| | | if (MRLRC_CURRENT_RECIPE_COMPLETE == nRet) { |
| | | lock(); |
| | | for (auto item : m_mapRecipes) { |
| | |
| | | if (m_nSyncStatus == SS_SYNCING) { |
| | | m_nTimeoutCount++; |
| | | if (m_nTimeoutCount > 10) { |
| | | m_nSyncStatus = SS_TIMEOUT; |
| | | unlock(); |
| | | syncTimeout(); |
| | | TRACE("CRecipesManager::TimeoutCheckWorkingProc 超时退出\n"); |
| | | lock(); |
| | | } |
| | | } |
| | | |
| | |
| | | unsigned TimeoutCheckWorkingProc(); |
| | | int syncing(); |
| | | void syncFailed(); |
| | | void syncTimeout(); |
| | | short decodeRecipeListReport(const char* pszData, size_t size); |
| | | short decodeRecipeParameterReport(const char* pszData, size_t size); |
| | | CRecipeList* getRecipeListFromTemp(int unitNo); |
| | |
| | | #include "afxdialogex.h" |
| | | #include "PageRecipe.h" |
| | | #include "MsgDlg.h" |
| | | |
| | | #include "RecipeDeviceBindDlg.h" |
| | | |
| | | // CPageRecipe 对话框 |
| | | |
| | |
| | | { |
| | | } |
| | | |
| | | void CPageRecipe::UpdateRecipeByPPID(const CString& strPPID) |
| | | { |
| | | if (strPPID.IsEmpty()) { |
| | | AfxMessageBox(_T("请选择一个配方!")); |
| | | return; |
| | | } |
| | | |
| | | auto& mgr = RecipeManager::getInstance(); |
| | | |
| | | // 查询选中配方的详细数据 |
| | | std::string oldPPID = CT2A(strPPID); |
| | | RecipeInfo oldRecipe = mgr.getRecipeByPPID(oldPPID); |
| | | if (oldRecipe.strPPID.empty()) { |
| | | AfxMessageBox(_T("获取配方数据失败!")); |
| | | return; |
| | | } |
| | | |
| | | // 弹出编辑对话框,并初始化为当前内容 |
| | | CRecipeDeviceBindDlg dlg(this); |
| | | dlg.SetRecipeInfo(oldRecipe); |
| | | |
| | | if (dlg.DoModal() == IDOK) { |
| | | const RecipeInfo& newRecipe = dlg.GetRecipeInfo(); |
| | | |
| | | bool success = false; |
| | | // 判断PPID是否有改动 |
| | | if (oldRecipe.strPPID != newRecipe.strPPID) { |
| | | // 先更新PPID,再整体更新内容 |
| | | if (mgr.updatePPID(oldRecipe.strPPID, newRecipe.strPPID)) { |
| | | success = mgr.updateRecipe(newRecipe); |
| | | if (!success) { |
| | | AfxMessageBox(_T("已更改PPID,但更新配方内容失败,请检查日志")); |
| | | } |
| | | } |
| | | else { |
| | | AfxMessageBox(_T("更新PPID失败,请检查日志")); |
| | | return; |
| | | } |
| | | } |
| | | else { |
| | | // 只更新内容 |
| | | success = mgr.updateRecipe(newRecipe); |
| | | if (!success) { |
| | | AfxMessageBox(_T("更新配方失败,请检查日志")); |
| | | } |
| | | } |
| | | |
| | | if (success) { |
| | | auto vecData = mgr.getAllRecipes(); |
| | | FillDataToListCtrl(vecData); |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::FillDataToListCtrl(const std::vector<RecipeInfo>& vecRecipe) { |
| | | CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_PPID); |
| | | if (pListCtrl == nullptr || !::IsWindow(pListCtrl->m_hWnd)) { |
| | |
| | | // 遍历数据并插入到CListCtrl中 |
| | | for (int i = 0; i < static_cast<int>(vecRecipe.size()); ++i) { |
| | | const RecipeInfo& recipe = vecRecipe[i]; |
| | | if (recipe.vecDeviceList.empty() || recipe.vecDeviceList.size() > 6){ |
| | | continue; |
| | | } |
| | | |
| | | m_listPPID.InsertItem(i, _T("")); // 第0列空白 |
| | | |
| | | CString strNo; |
| | | strNo.Format(_T("%d"), i + 1); |
| | | m_listPPID.SetItemText(i, 1, strNo); |
| | | |
| | | m_listPPID.SetItemText(i, 2, CA2T(recipe.strPPID.c_str())); |
| | | m_listPPID.SetItemText(i, 3, CA2T(recipe.strDescription.c_str())); |
| | | m_listPPID.SetItemText(i, 4, CA2T(recipe.strCreateTime.c_str())); |
| | | |
| | | for (int j = 0; j < recipe.vecDeviceList.size(); j++){ |
| | | CString str; |
| | | str.Format(_T("%d"), recipe.vecDeviceList.at(j).nRecipeID); |
| | | m_listPPID.SetItemText(i, j + 3, str); |
| | | } |
| | | |
| | | m_listPPID.SetItemText(i, 9, CA2T(recipe.strCreateTime.c_str())); |
| | | m_listPPID.SetItemText(i, 10, CA2T(recipe.strDescription.c_str())); |
| | | } |
| | | |
| | | // 获取列数 |
| | |
| | | if (pList == nullptr) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | // 遍历数据并插入到CListCtrl中 |
| | | std::map<int, short>& ids = pList->getIds(); |
| | |
| | | |
| | | BEGIN_MESSAGE_MAP(CPageRecipe, CDialogEx) |
| | | ON_WM_SIZE() |
| | | ON_WM_DESTROY() |
| | | ON_WM_SHOWWINDOW() |
| | | ON_BN_CLICKED(IDC_BUTTON_NEW, &CPageRecipe::OnBnClickedButtonNew) |
| | | ON_BN_CLICKED(IDC_BUTTON_SEARCH, &CPageRecipe::OnBnClickedButtonSearch) |
| | | ON_BN_CLICKED(IDC_BUTTON_MODIFY, &CPageRecipe::OnBnClickedButtonModify) |
| | | ON_BN_CLICKED(IDC_BUTTON_DELETE, &CPageRecipe::OnBnClickedButtonDelete) |
| | | ON_BN_CLICKED(IDC_BUTTON_DELETE_ALL, &CPageRecipe::OnBnClickedButtonDeleteAll) |
| | | ON_BN_CLICKED(IDC_BUTTON_REFRESH, &CPageRecipe::OnBnClickedButtonRefresh) |
| | | ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_PPID, &CPageRecipe::OnLvnItemChangedListPPID) |
| | | ON_WM_DESTROY() |
| | | ON_CBN_SELCHANGE(IDC_COMBO_EQUIPMENT, &CPageRecipe::OnCbnSelchangeComboEquipment) |
| | | ON_WM_SHOWWINDOW() |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | // 读出列宽 |
| | | CString strIniFile, strItem; |
| | | strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); |
| | | int width[8] = { 0, 80, 180, 80, 80, 100, 80, 180 }; |
| | | for (int i = 0; i < 8; i++) { |
| | | int width[12] = { 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 180 }; |
| | | for (int i = 0; i < 12; i++) { |
| | | strItem.Format(_T("Col_%d_Width"), i); |
| | | width[i] = GetPrivateProfileInt("PageRecipeListCtrl", strItem, width[i], strIniFile); |
| | | } |
| | | |
| | | |
| | | // TODO: 在此添加额外的初始化 |
| | | CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_PPID); |
| | |
| | | pListCtrl->InsertColumn(0, _T(""), LVCFMT_RIGHT, 0); // 隐藏列 |
| | | pListCtrl->InsertColumn(1, _T("No."), LVCFMT_LEFT, width[1]); |
| | | pListCtrl->InsertColumn(2, _T("PPID/Recipe ID"), LVCFMT_LEFT, width[2]); |
| | | pListCtrl->InsertColumn(3, _T("描述"), LVCFMT_LEFT, width[3]); |
| | | pListCtrl->InsertColumn(4, _T("创建时间"), LVCFMT_LEFT, width[4]); |
| | | pListCtrl->SetColumnWidth(4, LVSCW_AUTOSIZE_USEHEADER); |
| | | |
| | | pListCtrl->InsertColumn(3, _T("真空烘烤"), LVCFMT_LEFT, width[6]); |
| | | pListCtrl->InsertColumn(4, _T("Bonder1"), LVCFMT_LEFT, width[4]); |
| | | pListCtrl->InsertColumn(5, _T("Bonder2"), LVCFMT_LEFT, width[5]); |
| | | pListCtrl->InsertColumn(6, _T("后烘冷却"), LVCFMT_LEFT, width[7]); |
| | | pListCtrl->InsertColumn(7, _T("精度检查"), LVCFMT_LEFT, width[8]); |
| | | pListCtrl->InsertColumn(8, _T("EFEM"), LVCFMT_LEFT, width[3]); |
| | | pListCtrl->InsertColumn(9, _T("创建时间"), LVCFMT_LEFT, width[9]); |
| | | pListCtrl->InsertColumn(10, _T("描述"), LVCFMT_LEFT, width[10]); |
| | | pListCtrl->SetColumnWidth(10, LVSCW_AUTOSIZE_USEHEADER); |
| | | |
| | | // 获取所有数据 |
| | | auto vecData = RecipeManager::getInstance().getAllRecipes(); |
| | |
| | | // 按钮竖直排列在右侧 |
| | | CWnd* buttons[] = { |
| | | GetDlgItem(IDC_BUTTON_REFRESH), |
| | | GetDlgItem(IDC_BUTTON_NEW), |
| | | GetDlgItem(IDC_BUTTON_MODIFY), |
| | | GetDlgItem(IDC_BUTTON_DELETE), |
| | | GetDlgItem(IDC_BUTTON_DELETE_ALL), |
| | | GetDlgItem(IDC_BUTTON_MODIFY) |
| | | GetDlgItem(IDC_BUTTON_DELETE_ALL) |
| | | }; |
| | | |
| | | for (auto pBtn : buttons) { |
| | |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnDestroy() |
| | | { |
| | | CDialogEx::OnDestroy(); |
| | | |
| | | // 保存列宽 |
| | | CString strIniFile, strItem, strTemp; |
| | | strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); |
| | | CHeaderCtrl* pHeader = m_listPPID.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("PageRecipeListCtrl", strItem, strTemp, strIniFile); |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnShowWindow(BOOL bShow, UINT nStatus) |
| | | { |
| | | CDialogEx::OnShowWindow(bShow, nStatus); |
| | | |
| | | if (bShow) { |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT); |
| | | if (pComboBox->GetCount() == 0) { |
| | | SERVO::CMaster& master = theApp.m_model.getMaster(); |
| | | SERVO::CEquipment* pEq[] = { |
| | | nullptr, |
| | | master.getEquipment(EQ_ID_EFEM), |
| | | master.getEquipment(EQ_ID_Bonder1), |
| | | master.getEquipment(EQ_ID_Bonder2), |
| | | master.getEquipment(EQ_ID_BAKE_COOLING), |
| | | master.getEquipment(EQ_ID_VACUUMBAKE), |
| | | master.getEquipment(EQ_ID_MEASUREMENT), |
| | | }; |
| | | |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT); |
| | | for (int i = 0; i < sizeof(pEq) / sizeof(pEq[0]); i++) { |
| | | pComboBox->InsertString(i, |
| | | pEq[i] == nullptr ? _T("Master") : pEq[i]->getName().c_str()); |
| | | pComboBox->SetItemDataPtr(i, pEq[i]); |
| | | } |
| | | pComboBox->SetCurSel(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonNew() |
| | | { |
| | | // TODO: 在此添加控件通知处理程序代码 |
| | | //CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT); |
| | | //int nSel = pComboBox->GetCurSel(); |
| | | //SERVO::CEquipment* pEq = (SERVO::CEquipment*)pComboBox->GetItemDataPtr(nSel); |
| | | //if (pEq == nullptr) { |
| | | // return; |
| | | //} |
| | | |
| | | CRecipeDeviceBindDlg dlg(this); |
| | | if (dlg.DoModal() == IDOK) { |
| | | const RecipeInfo& newRecipe = dlg.GetRecipeInfo(); |
| | | |
| | | auto& mgr = RecipeManager::getInstance(); |
| | | if (mgr.ppidExists(newRecipe.strPPID)) { |
| | | // 已存在,询问是否覆盖 |
| | | int ret = AfxMessageBox(_T("该 PPID 已存在,是否覆盖原配方?"), MB_YESNO | MB_ICONQUESTION); |
| | | if (ret == IDYES) { |
| | | if (mgr.updateRecipe(newRecipe)) { |
| | | auto vecData = mgr.getAllRecipes(); |
| | | FillDataToListCtrl(vecData); |
| | | } |
| | | else { |
| | | AfxMessageBox(_T("更新配方失败,请检查日志")); |
| | | } |
| | | } |
| | | } |
| | | else { |
| | | // 不存在,直接新增 |
| | | if (mgr.addRecipe(newRecipe)) { |
| | | auto vecData = mgr.getAllRecipes(); |
| | | FillDataToListCtrl(vecData); |
| | | } |
| | | else { |
| | | AfxMessageBox(_T("添加配方失败,请检查日志")); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonSearch() |
| | | { |
| | | CString strKeyword; |
| | | GetDlgItemText(IDC_EDIT_KEYWORD, strKeyword); |
| | | AfxMessageBox(strKeyword); |
| | | strKeyword.Trim(); |
| | | |
| | | std::vector<RecipeInfo> vecData; |
| | | if (strKeyword.IsEmpty()) { |
| | | // 关键词为空,显示全部配方 |
| | | vecData = RecipeManager::getInstance().getAllRecipes(); |
| | | } |
| | | else { |
| | | // 根据关键词搜索配方 |
| | | vecData = RecipeManager::getInstance().getRecipesByKeyword(std::string(CT2A(strKeyword))); |
| | | } |
| | | |
| | | // 如果没有数据,弹出提示 |
| | | if (vecData.empty()) { |
| | | AfxMessageBox(_T("未找到匹配的配方!")); |
| | | return; |
| | | } |
| | | |
| | | FillDataToListCtrl(vecData); |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonModify() |
| | | { |
| | | // TODO: 在此添加控件通知处理程序代码 |
| | | /* |
| | | POSITION pos = m_listPPID.GetFirstSelectedItemPosition(); |
| | | if (!pos) { |
| | | AfxMessageBox(_T("请选择要修改的配方")); |
| | | AfxMessageBox(_T("请先选择一条配方记录进行修改!")); |
| | | return; |
| | | } |
| | | |
| | | int nSel = m_listPPID.GetNextSelectedItem(pos); |
| | | CString strOldPPID = m_listPPID.GetItemText(nSel, 2); |
| | | CString strOldDesc = m_listPPID.GetItemText(nSel, 3); |
| | | |
| | | CString strNewPPID, strNewDesc; |
| | | m_editPPID.GetWindowText(strNewPPID); |
| | | m_editDesc.GetWindowText(strNewDesc); |
| | | |
| | | // 判空 |
| | | if (strOldPPID.IsEmpty() || strNewPPID.IsEmpty()) { |
| | | AfxMessageBox(_T("PPID 不能为空")); |
| | | return; |
| | | } |
| | | |
| | | std::string oldPPID = CT2A(strOldPPID); |
| | | std::string newPPID = CT2A(strNewPPID); |
| | | std::string newDesc = CT2A(strNewDesc); |
| | | |
| | | bool bPPIDChanged = (strOldPPID.Compare(strNewPPID) != 0); |
| | | bool bDescChanged = (strOldDesc.Compare(strNewDesc) != 0); |
| | | |
| | | if (!bPPIDChanged && !bDescChanged) { |
| | | return; |
| | | } |
| | | |
| | | if (bPPIDChanged) { |
| | | // 新 PPID 不可重复 |
| | | if (RecipeManager::getInstance().ppidExists(newPPID)) { |
| | | AfxMessageBox(_T("新 PPID 已存在,请使用其他值")); |
| | | return; |
| | | } |
| | | |
| | | // 调用 updatePPID,同时更新描述 |
| | | if (RecipeManager::getInstance().updatePPID(oldPPID, newPPID)) { |
| | | m_listPPID.SetItemText(nSel, 2, strNewPPID); |
| | | } |
| | | else { |
| | | AfxMessageBox(_T("更新失败,请检查日志")); |
| | | } |
| | | } |
| | | |
| | | if (bDescChanged) { |
| | | // 更新描述 |
| | | if (RecipeManager::getInstance().updateDescription(oldPPID, newDesc)) { |
| | | m_listPPID.SetItemText(nSel, 3, strNewDesc); |
| | | } |
| | | else { |
| | | AfxMessageBox(_T("描述更新失败")); |
| | | } |
| | | } |
| | | */ |
| | | CString strPPID = m_listPPID.GetItemText(nSel, 2); |
| | | UpdateRecipeByPPID(strPPID); |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonDelete() |
| | |
| | | // enable port |
| | | CMsgDlg msgDlg("请等待", "正在获取配方..."); |
| | | pEq->masterRecipeListRequest(0, [&](int status) -> void { |
| | | if (status == SS_FAILED) { |
| | | if (status == SS_FAILED || status == SS_TIMEOUT) { |
| | | CString strMsg; |
| | | strMsg.Format(_T("获取配方失败!")); |
| | | strMsg.Format(status == SS_FAILED ? _T("获取配方失败!") : _T("获取配方超时!")); |
| | | msgDlg.DelayClose(3000); |
| | | msgDlg.SetIcon(MSG_BOX_ERROR); |
| | | msgDlg.SetTitle(_T("操作失败")); |
| | |
| | | msgDlg.SetMarquee(FALSE, 0); |
| | | msgDlg.SetCompleteCode(0); |
| | | } |
| | | }); |
| | | }); |
| | | msgDlg.DoModal(); |
| | | } |
| | | } |
| | |
| | | else { |
| | | SERVO::CRecipeList* pRecipeList = pEq->getRecipeList(0); |
| | | FillRecipeListToListCtrl(pRecipeList); |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| | | |
| | | |
| | | void CPageRecipe::OnDestroy() |
| | | { |
| | | CDialogEx::OnDestroy(); |
| | | |
| | | // 保存列宽 |
| | | CString strIniFile, strItem, strTemp; |
| | | strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir); |
| | | CHeaderCtrl* pHeader = m_listPPID.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("PageRecipeListCtrl", strItem, strTemp, strIniFile); |
| | | } |
| | | } |
| | | |
| | | |
| | | void CPageRecipe::OnShowWindow(BOOL bShow, UINT nStatus) |
| | | { |
| | | CDialogEx::OnShowWindow(bShow, nStatus); |
| | | |
| | | if (bShow) { |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT); |
| | | if (pComboBox->GetCount() == 0) { |
| | | SERVO::CMaster& master = theApp.m_model.getMaster(); |
| | | SERVO::CEquipment* pEq[] = { |
| | | nullptr, |
| | | master.getEquipment(EQ_ID_EFEM), |
| | | master.getEquipment(EQ_ID_Bonder1), |
| | | master.getEquipment(EQ_ID_Bonder2), |
| | | master.getEquipment(EQ_ID_BAKE_COOLING), |
| | | master.getEquipment(EQ_ID_VACUUMBAKE), |
| | | master.getEquipment(EQ_ID_MEASUREMENT), |
| | | }; |
| | | |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT); |
| | | for (int i = 0; i < sizeof(pEq) / sizeof(pEq[0]); i++) { |
| | | pComboBox->InsertString(i, |
| | | pEq[i] == nullptr ? _T("Master") : pEq[i]->getName().c_str()); |
| | | pComboBox->SetItemDataPtr(i, pEq[i]); |
| | | } |
| | | pComboBox->SetCurSel(0); |
| | | } |
| | | } |
| | | } |
| | |
| | | virtual ~CPageRecipe(); |
| | | |
| | | private: |
| | | void UpdateRecipeByPPID(const CString& strPPID); |
| | | void FillDataToListCtrl(const std::vector<RecipeInfo>& vecRecipe); |
| | | void FillRecipeListToListCtrl(SERVO::CRecipeList* pList); |
| | | |
| | |
| | | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 |
| | | virtual BOOL OnInitDialog(); |
| | | afx_msg void OnSize(UINT nType, int cx, int cy); |
| | | afx_msg void OnDestroy(); |
| | | afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); |
| | | afx_msg void OnBnClickedButtonNew(); |
| | | afx_msg void OnBnClickedButtonSearch(); |
| | | afx_msg void OnBnClickedButtonModify(); |
| | | afx_msg void OnBnClickedButtonDelete(); |
| | | afx_msg void OnBnClickedButtonDeleteAll(); |
| | | afx_msg void OnBnClickedButtonRefresh(); |
| | | afx_msg void OnLvnItemChangedListPPID(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnCbnSelchangeComboEquipment(); |
| | | DECLARE_MESSAGE_MAP() |
| | | |
| | | private: |
| | | CListCtrl m_listPPID; |
| | | public: |
| | | afx_msg void OnDestroy(); |
| | | afx_msg void OnCbnSelchangeComboEquipment(); |
| | | afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); |
| | | }; |
| | |
| | | pJobDataS->setProductId(config.strProductID.c_str()); |
| | | pJobDataS->setOperationId(config.strOperationID.c_str()); |
| | | pJobDataS->setMaterialsType(config.nMaterialType); |
| | | |
| | | RecipeInfo stRecipeInfo = RecipeManager::getInstance().getRecipeByPPID(config.strRecipe); |
| | | std::vector<DeviceRecipe> vecRecipeInfo = stRecipeInfo.vecDeviceList; |
| | | |
| | | for (const auto& info : vecRecipeInfo) { |
| | | const std::string& name = info.strDeviceName; |
| | | short nRecipeID = (short)info.nRecipeID; |
| | | |
| | | if (name == EQ_NAME_EFEM) { |
| | | pJobDataS->setDeviceRecipeId(0, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_BONDER1) { |
| | | pJobDataS->setDeviceRecipeId(1, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_BONDER2) { |
| | | pJobDataS->setDeviceRecipeId(2, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_BAKE_COOLING) { |
| | | pJobDataS->setDeviceRecipeId(3, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_VACUUMBAKE) { |
| | | pJobDataS->setDeviceRecipeId(4, nRecipeID); |
| | | } |
| | | else if (name == EQ_NAME_MEASUREMENT) { |
| | | pJobDataS->setDeviceRecipeId(5, nRecipeID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | CRecipeDeviceBindDlg::CRecipeDeviceBindDlg(CWnd* pParent /*=nullptr*/) |
| | | : CDialogEx(IDD_DIALOG_RECIPE_DEVICE_BIND, pParent) |
| | | , m_strPPID(_T("")) |
| | | , m_strDesc(_T("")) |
| | | { |
| | | |
| | | } |
| | |
| | | { |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::DoDataExchange(CDataExchange* pDX) |
| | | { |
| | | CDialogEx::DoDataExchange(pDX); |
| | | const RecipeInfo& CRecipeDeviceBindDlg::GetRecipeInfo() const { |
| | | return m_recipe; |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::SetRecipeInfo(const RecipeInfo& info) |
| | | { |
| | | m_recipe = info; |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::ReleaseDeviceControls() |
| | | { |
| | | for (auto& ctrl : m_vecDevices) { |
| | | delete ctrl.editDeviceID; ctrl.editDeviceID = nullptr; |
| | | delete ctrl.editDeviceName; ctrl.editDeviceName = nullptr; |
| | | delete ctrl.comboRecipeID; ctrl.comboRecipeID = nullptr; |
| | | } |
| | | m_vecDevices.clear(); |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::CreateDeviceControls(int nXStart, int nYStart, int nTotalControlWidth, int nRowHeight) |
| | | { |
| | | for (size_t i = 0; i < g_vecBindDevices.size(); ++i) { |
| | | int y = nYStart + static_cast<int>(i) * nRowHeight; |
| | | auto* pEditID = new CEdit; |
| | | pEditID->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER, CRect(nXStart, y, nXStart + 100, y + 25), this, (UINT)(IDC_EDIT_DEVICEID_BASE + i)); |
| | | pEditID->SetFont(&m_font); |
| | | |
| | | auto* pEditName = new CEdit; |
| | | pEditName->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER, CRect(nXStart + 110, y, nXStart + 210, y + 25), this, (UINT)(IDC_EDIT_DEVICENAME_BASE + i)); |
| | | pEditName->SetFont(&m_font); |
| | | |
| | | auto* pCombo = new CComboBox; |
| | | pCombo->Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, CRect(nXStart + 220, y, nXStart + nTotalControlWidth, y + 25), this, (UINT)(IDC_COMBO_RECIPEID_BASE + i)); |
| | | pCombo->SetFont(&m_font); |
| | | |
| | | m_vecDevices.push_back({ pEditID, pEditName, pCombo }); |
| | | } |
| | | } |
| | | |
| | | bool CRecipeDeviceBindDlg::FillComboRecipeList(CComboBox* pCombo, int nDeviceID, int nSelectedRecipeID) |
| | | { |
| | | auto& master = theApp.m_model.getMaster(); |
| | | auto* pEq = master.getEquipment(nDeviceID); |
| | | if (!pEq) { |
| | | return false; |
| | | } |
| | | |
| | | auto* pRecipeList = pEq->getRecipeList(0); |
| | | if (!pRecipeList) { |
| | | return false; |
| | | } |
| | | |
| | | auto& mapRecipeIds = pRecipeList->getIds(); |
| | | bool bFound = false; |
| | | pCombo->ResetContent(); |
| | | for (const auto& pair : mapRecipeIds) { |
| | | int nRecipeID = pair.second; |
| | | CString strRecipeName; |
| | | strRecipeName.Format(_T("%d"), nRecipeID); |
| | | int idx = pCombo->AddString(strRecipeName); |
| | | pCombo->SetItemData(idx, nRecipeID); |
| | | if (nSelectedRecipeID == nRecipeID) { |
| | | pCombo->SetCurSel(idx); |
| | | bFound = true; |
| | | } |
| | | } |
| | | |
| | | if (nSelectedRecipeID != -1 && !bFound) { |
| | | pCombo->SetCurSel(CB_ERR); |
| | | } |
| | | else if (pCombo->GetCount() > 0 && nSelectedRecipeID == -1) { |
| | | pCombo->SetCurSel(0); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool CRecipeDeviceBindDlg::FillDeviceInfo(int idx, int nDeviceID, const CString& strDeviceName, int nSelectedRecipeID) |
| | | { |
| | | if (idx < 0 || idx >= (int)m_vecDevices.size()) { |
| | | return false; |
| | | } |
| | | |
| | | auto& ctrl = m_vecDevices[idx]; |
| | | CString strID; |
| | | strID.Format(_T("%d"), nDeviceID); |
| | | ctrl.editDeviceID->SetWindowText(strID); |
| | | ctrl.editDeviceID->SetReadOnly(TRUE); |
| | | ctrl.editDeviceName->SetWindowText(strDeviceName); |
| | | ctrl.editDeviceName->SetReadOnly(TRUE); |
| | | |
| | | if (!FillComboRecipeList(ctrl.comboRecipeID, nDeviceID, nSelectedRecipeID)) { |
| | | CString str; |
| | | str.Format(_T("设备 [%s] 或其配方列表未找到,请检查设备配置"), strDeviceName.GetString()); |
| | | AfxMessageBox(str); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::DoDataExchange(CDataExchange* pDX) |
| | | { |
| | | CDialogEx::DoDataExchange(pDX); |
| | | DDX_Text(pDX, IDC_EDIT_PPID, m_strPPID); |
| | | DDX_Text(pDX, IDC_EDIT_DESC, m_strDesc); |
| | | } |
| | | |
| | | BEGIN_MESSAGE_MAP(CRecipeDeviceBindDlg, CDialogEx) |
| | | ON_WM_CLOSE() |
| | | ON_WM_SIZE() |
| | | ON_BN_CLICKED(IDOK, &CRecipeDeviceBindDlg::OnBnClickedOk) |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | |
| | | { |
| | | CDialogEx::OnInitDialog(); |
| | | |
| | | // TODO: 在此添加额外的初始化 |
| | | // 设置固定大小(例如 600x400) |
| | | SetWindowPos(nullptr, 0, 0, 600, 400, SWP_NOMOVE | SWP_NOZORDER); |
| | | // 设置对话框标题 |
| | | SetWindowText(m_recipe.vecDeviceList.empty() ? _T("新建配方") : _T("编辑配方")); |
| | | |
| | | // 创建控件 |
| | | const int totalControlWidth = 340; |
| | | CRect clientRect; |
| | | GetClientRect(&clientRect); |
| | | int xStart = (clientRect.Width() - totalControlWidth) / 2; |
| | | |
| | | const int nRowHeight = 30; |
| | | const int yStart = 30; // 顶部起始高度 |
| | | |
| | | const int nRowCount = static_cast<int>(g_vecBindDevices.size()); |
| | | for (int i = 0; i < nRowCount; ++i) { |
| | | int y = yStart + i * nRowHeight; |
| | | const auto& meta = g_vecBindDevices[i]; |
| | | |
| | | CEdit* pEditID = new CEdit(); |
| | | pEditID->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(xStart, y, xStart + 100, y + 25), this, IDC_EDIT_DEVICEID_BASE + i); |
| | | |
| | | CString strID; |
| | | strID.Format(_T("%d"), meta.nDeviceID); |
| | | pEditID->SetWindowText(strID); |
| | | pEditID->SetReadOnly(TRUE); // 设备ID只读 |
| | | |
| | | CEdit* pEditName = new CEdit(); |
| | | pEditName->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(xStart + 110, y, xStart + 210, y + 25), this, IDC_EDIT_DEVICENAME_BASE + i); |
| | | pEditName->SetWindowText(CA2T(meta.strDeviceName)); |
| | | pEditName->SetReadOnly(TRUE); // 设备名称只读 |
| | | |
| | | CComboBox* pCombo = new CComboBox(); |
| | | pCombo->Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, CRect(xStart + 220, y, xStart + 340, y + 300), this, IDC_COMBO_RECIPEID_BASE + i); |
| | | |
| | | // 添加选项到 ComboBox |
| | | m_vecDevices.push_back({ pEditID, pEditName, pCombo }); |
| | | // 创建动态控件字体 |
| | | if (!m_font.m_hObject) { |
| | | CFont* pDlgFont = GetFont(); |
| | | LOGFONT lf; |
| | | if (pDlgFont && pDlgFont->GetLogFont(&lf)) { |
| | | lf.lfHeight = -16; |
| | | m_font.CreateFontIndirect(&lf); |
| | | } |
| | | } |
| | | |
| | | // 计算坐标 |
| | | CRect rDesc; |
| | | int nXStart = 30, nYStart = 30, nTotalControlWidth = 340; |
| | | if (auto* pWndDesc = GetDlgItem(IDC_STATIC_DESC)) { |
| | | pWndDesc->GetWindowRect(&rDesc); ScreenToClient(&rDesc); |
| | | nXStart = rDesc.left; |
| | | } |
| | | if (auto* pWndEdit = GetDlgItem(IDC_EDIT_DESC)) { |
| | | pWndEdit->GetWindowRect(&rDesc); ScreenToClient(&rDesc); |
| | | nYStart = rDesc.bottom + 20; |
| | | } |
| | | CRect rClient; GetClientRect(&rClient); |
| | | nTotalControlWidth = rClient.Width() - nXStart * 2; |
| | | const int nRowHeight = 30; |
| | | |
| | | // 清空旧控件 |
| | | ReleaseDeviceControls(); |
| | | |
| | | // 创建新控件 |
| | | CreateDeviceControls(nXStart, nYStart, nTotalControlWidth, nRowHeight); |
| | | |
| | | auto& master = theApp.m_model.getMaster(); |
| | | |
| | | // 填充内容 |
| | | if (m_recipe.vecDeviceList.empty()) { |
| | | // 新建 |
| | | for (size_t i = 0; i < g_vecBindDevices.size(); ++i) { |
| | | const auto& meta = g_vecBindDevices[i]; |
| | | FillDeviceInfo((int)i, meta.nDeviceID, meta.strDeviceName); |
| | | } |
| | | } |
| | | else { |
| | | // 编辑 |
| | | m_strPPID = CA2T(m_recipe.strPPID.c_str()); |
| | | m_strDesc = CA2T(m_recipe.strDescription.c_str()); |
| | | UpdateData(FALSE); |
| | | |
| | | for (size_t i = 0; i < m_recipe.vecDeviceList.size() && i < m_vecDevices.size(); ++i) { |
| | | const auto& d = m_recipe.vecDeviceList[i]; |
| | | FillDeviceInfo((int)i, d.nDeviceID, d.strDeviceName.c_str(), d.nRecipeID); |
| | | } |
| | | } |
| | | |
| | | CenterWindow(); |
| | | |
| | | return TRUE; // return TRUE unless you set the focus to a control |
| | | // 异常: OCX 属性页应返回 FALSE |
| | |
| | | CDialogEx::OnClose(); |
| | | |
| | | // 清理控件 |
| | | for (auto& device : m_vecDevices) { |
| | | if (device.editDeviceID) { |
| | | device.editDeviceID->DestroyWindow(); |
| | | delete device.editDeviceID; |
| | | } |
| | | if (device.editDeviceName) { |
| | | device.editDeviceName->DestroyWindow(); |
| | | delete device.editDeviceName; |
| | | } |
| | | if (device.comboRecipeID) { |
| | | device.comboRecipeID->DestroyWindow(); |
| | | delete device.comboRecipeID; |
| | | } |
| | | } |
| | | m_vecDevices.clear(); |
| | | ReleaseDeviceControls(); |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::OnSize(UINT nType, int cx, int cy) |
| | |
| | | CDialogEx::OnSize(nType, cx, cy); |
| | | |
| | | // TODO: 在此处添加消息处理程序代码 |
| | | } |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::OnBnClickedOk() |
| | | { |
| | | // TODO: 在此添加控件通知处理程序代码 |
| | | UpdateData(TRUE); |
| | | |
| | | // 收集所有设备映射 |
| | | m_recipe.vecDeviceList.clear(); |
| | | for (const auto& dev : m_vecDevices) { |
| | | DeviceRecipe info; |
| | | CString strID, strName; |
| | | dev.editDeviceID->GetWindowText(strID); |
| | | dev.editDeviceName->GetWindowText(strName); |
| | | |
| | | int sel = dev.comboRecipeID->GetCurSel(); |
| | | info.nRecipeID = -1; |
| | | if (sel != CB_ERR) { |
| | | info.nRecipeID = (int)dev.comboRecipeID->GetItemData(sel); |
| | | } |
| | | info.nDeviceID = _ttoi(strID); |
| | | info.strDeviceName = CT2A(strName); |
| | | |
| | | m_recipe.vecDeviceList.push_back(info); |
| | | } |
| | | |
| | | // 检查 PPID 是否为空 |
| | | if (m_strPPID.IsEmpty()) { |
| | | AfxMessageBox(_T("配方 PPID 不能为空")); |
| | | return; |
| | | } |
| | | |
| | | // PPID和描述 |
| | | m_recipe.strPPID = CT2A(m_strPPID); |
| | | m_recipe.strDescription = CT2A(m_strDesc); |
| | | |
| | | CDialogEx::OnOK(); |
| | | } |
| | |
| | | #pragma once |
| | | #include "afxdialogex.h" |
| | | |
| | | #include "RecipeManager.h" |
| | | |
| | | // CRecipeDeviceBindDlg 对话框 |
| | | |
| | |
| | | CRecipeDeviceBindDlg(CWnd* pParent = nullptr); // 标准构造函数 |
| | | virtual ~CRecipeDeviceBindDlg(); |
| | | |
| | | const RecipeInfo& GetRecipeInfo() const; |
| | | void SetRecipeInfo(const RecipeInfo& info); |
| | | |
| | | // 对话框数据 |
| | | #ifdef AFX_DESIGN_TIME |
| | | enum { IDD = IDD_DIALOG_RECIPE_DEVICE_BIND }; |
| | |
| | | virtual BOOL OnInitDialog(); |
| | | afx_msg void OnClose(); |
| | | afx_msg void OnSize(UINT nType, int cx, int cy); |
| | | afx_msg void OnBnClickedOk(); |
| | | DECLARE_MESSAGE_MAP() |
| | | |
| | | private: |
| | | void ReleaseDeviceControls(); |
| | | void CreateDeviceControls(int nXStart, int nYStart, int nTotalControlWidth, int nRowHeight); |
| | | bool FillDeviceInfo(int idx, int nDeviceID, const CString& strDeviceName, int nSelectedRecipeID = -1); |
| | | bool FillComboRecipeList(CComboBox* pCombo, int nDeviceID, int nSelectedRecipeID = -1); |
| | | |
| | | struct DeviceWidget { |
| | | CEdit* editDeviceID; |
| | | CEdit* editDeviceName; |
| | | CComboBox* comboRecipeID; |
| | | }; |
| | | |
| | | CFont m_font; |
| | | CString m_strPPID; |
| | | CString m_strDesc; |
| | | RecipeInfo m_recipe; |
| | | std::vector<DeviceWidget> m_vecDevices; |
| | | }; |
| | |
| | | |
| | | for (const auto& dev : devs) { |
| | | DeviceRecipe dr; |
| | | dr.strPPID = info.strPPID; |
| | | try { |
| | | dr.nDeviceID = std::stoi(dev[0]); |
| | | dr.strDeviceName = dev[1]; |
| | |
| | | auto devs = m_pDB->fetchResults("SELECT device_id, device_name, recipe_id FROM recipe_devices WHERE ppid = '" + ppid + "';"); |
| | | for (const auto& dev : devs) { |
| | | DeviceRecipe dr; |
| | | dr.strPPID = ppid; |
| | | try { |
| | | dr.nDeviceID = std::stoi(dev[0]); |
| | | dr.strDeviceName = dev[1]; |
| | |
| | | recipe.strDescription = "Main Board Burn-in"; |
| | | |
| | | recipe.vecDeviceList = { |
| | | {1, 101, "P1001","Burner A"}, |
| | | {2, 102, "P1001", "Burner B"} |
| | | {1, 101, "Burner A"}, |
| | | {2, 102, "Burner B"} |
| | | }; |
| | | |
| | | addRecipe(recipe); |
| | |
| | | std::getline(ss, description, ','); |
| | | std::getline(ss, createTime, ','); |
| | | |
| | | dev.strPPID = ppid; |
| | | auto& recipe = recipeMap[ppid]; |
| | | recipe.strPPID = ppid; |
| | | recipe.strDescription = description; |
| | |
| | | // 单个设备配方映射信息 |
| | | struct DeviceRecipe { |
| | | int nDeviceID; // 设备ID |
| | | int nRecipeID; // 该设备对应的子配方ID |
| | | std::string strPPID; // 配方ID(主键) |
| | | int nRecipeID; // 子配方ID |
| | | std::string strDeviceName; // 设备名称 |
| | | }; |
| | | |