| | |
| | | |
| | | void CAttributeVector::addAttribute(CAttribute* pAttribute, BOOL bReplace/* = FALSE*/) |
| | | { |
| | | if (!pAttribute) { |
| | | return; |
| | | } |
| | | |
| | | const std::string& name = pAttribute->getName(); |
| | | if (name.empty()) { |
| | | return; |
| | | } |
| | | |
| | | if (bReplace) { |
| | | for (auto it = m_attributes.begin(); it != m_attributes.end(); ) { |
| | | if ((*it)->getName().compare(pAttribute->getName()) == 0) { |
| | | if (name.compare((*it)->getName()) == 0) { |
| | | delete (*it); |
| | | it = m_attributes.erase(it); |
| | | } |
| | |
| | | CEqReadStep* pStep = new CEqReadStep(0x6301, 108 * 2, |
| | | [&](void* pFrom, int code, const char* pszData, size_t size) -> int { |
| | | if (code == ROK && pszData != nullptr && size > 0) { |
| | | decodePanelDataRequest((CStep*)pFrom, pszData, size); |
| | | decodeFacDataReport((CStep*)pFrom, pszData, size); |
| | | } |
| | | return -1; |
| | | }); |
| | |
| | | } |
| | | |
| | | { |
| | | // Panel Data Request |
| | | // JOB Data Request |
| | | CEqReadStep* pStep = new CEqReadStep(0x617d, 2 * 2, |
| | | [&](void* pFrom, int code, const char* pszData, size_t size) -> int { |
| | | if (code == ROK && pszData != nullptr && size > 0) { |
| | | decodePanelDataRequest((CStep*)pFrom, pszData, size); |
| | | decodeJobDataRequest((CStep*)pFrom, pszData, size); |
| | | |
| | | // efmeï¼ è·åæ°æ®åè¿å |
| | | // Cassette Sequence No 1W |
| | | // Job Sequence No 1W |
| | | // Job DataS 256W |
| | | char szBuffer[1024] = { 0 }; |
| | | CJobDataS* pJobDataS = m_pPort[3]->getJobDataSWithCassette(4001, 1); |
| | | if (pJobDataS != nullptr) { |
| | | int size = pJobDataS->serialize(szBuffer, 1024); |
| | | ((CEqReadStep*)pFrom)->setReturnData(szBuffer, size); |
| | | } |
| | | } |
| | | return -1; |
| | | }); |
| | | pStep->setName(STEP_EFEM_PANEL_DATA_REQUEST); |
| | | pStep->setProp("Port", (void*)1); |
| | | pStep->setWriteSignalDev(0x15d); |
| | | pStep->setReturnDev(0x73a); |
| | | if (addStep(STEP_ID_PANEL_DATA_REQUEST, pStep) != 0) { |
| | | pStep->setWriteSignalDev(0x35); |
| | | pStep->setReturnDev(0x5EA); |
| | | if (addStep(STEP_ID_JOB_DATA_REQUEST, pStep) != 0) { |
| | | delete pStep; |
| | | } |
| | | } |
| | |
| | | |
| | | // robot cmd reply |
| | | CHECK_WRITE_STEP_SIGNAL(STEP_ID_ROBOT_CMD_REPLY, pszData, size); |
| | | |
| | | |
| | | // Indexer Operation Mode Change |
| | | CHECK_WRITE_STEP_SIGNAL(STEP_ID_IN_OP_CMD_REPLY, pszData, size); |
| | | |
| | | // Panel Data Report |
| | | CHECK_WRITE_STEP_SIGNAL(STEP_ID_PANEL_DATA_REPORT, pszData, size); |
| | | |
| | | // Panel Data Request |
| | | CHECK_WRITE_STEP_SIGNAL(STEP_ID_PANEL_DATA_REQUEST, pszData, size); |
| | | |
| | | // Job Data Request |
| | | CHECK_READ_STEP_SIGNAL(STEP_ID_JOB_DATA_REQUEST, pszData, size); |
| | | } |
| | | |
| | | BOOL CEquipment::isBitOn(const char* pszData, size_t size, int index) |
| | |
| | | |
| | | unsigned short operationMode = (unsigned short)((unsigned short)mode + getIndexerOperationModeBaseValue()); |
| | | LOGI("<CEquipment-%s>åå¤è®¾ç½®indexerOperationMode<%d>", m_strName.c_str(), (int)mode); |
| | | pStep->writeShort(operationMode, [&, mode, onWritedRetBlock](int code) -> int { |
| | | pStep->writeShort(operationMode, [&, pStep, mode, onWritedRetBlock](int code) -> int { |
| | | int retCode = 0; |
| | | if (code == WOK) { |
| | | LOGI("<CEquipment-%s>设置indexerOperationModeæå.", m_strName.c_str()); |
| | | const char* pszRetData = nullptr; |
| | | pStep->getReturnData(pszRetData); |
| | | ASSERT(pszRetData); |
| | | retCode = (unsigned int)CToolUnits::toInt16(pszRetData); |
| | | retCode = (unsigned int)CToolUnits::toInt16(pszRetData); |
| | | LOGI("<CEquipment-%s>è¿åå¼: %d", m_strName.c_str(), retCode); |
| | | } |
| | | else { |
| | | LOGI("<CEquipment-%s>设置indexerOperationMode失败ï¼code:%d", m_strName.c_str(), code); |
| | |
| | | return 0; |
| | | } |
| | | |
| | | int CEquipment::decodePanelDataRequest(CStep* pStep, const char* pszData, size_t size) |
| | | int CEquipment::decodeJobDataRequest(CStep* pStep, const char* pszData, size_t size) |
| | | { |
| | | int index = 0; |
| | | short cassetteSequenceNo, jobSequenceNo; |
| | |
| | | index += sizeof(short); |
| | | memcpy(&jobSequenceNo, &pszData[index], sizeof(short)); |
| | | index += sizeof(short); |
| | | cassetteSequenceNo = 4000; |
| | | jobSequenceNo = 1; |
| | | |
| | | |
| | | // efmeï¼ è·åæ°æ®åè¿å |
| | | // Cassette Sequence No 1W |
| | | // Job Sequence No 1W |
| | | // Job DataS 256W |
| | | char szBuffer[1024]; |
| | | index = 0; |
| | | memcpy(&szBuffer[index], &cassetteSequenceNo, sizeof(short)); |
| | | index += sizeof(short); |
| | | memcpy(&szBuffer[index], &jobSequenceNo, sizeof(short)); |
| | | index += sizeof(short); |
| | | |
| | | CJobDataS* pJobDataS = getJobDataSWithCassette(cassetteSequenceNo, jobSequenceNo); |
| | | if (pJobDataS != nullptr) { |
| | | index += pJobDataS->serialize(&szBuffer[index], 1024 - sizeof(short) - sizeof(short)); |
| | | ((CEqReadStep*)pStep)->setReturnData(szBuffer, index); |
| | | } |
| | | |
| | | |
| | | // ç¼åAttributeï¼ç¨äºè°è¯æ¶æ¾ç¤ºä¿¡æ¯ |
| | |
| | | int decodeVCREventReport(CStep* pStep, const char* pszData, size_t size); |
| | | int decodePanelDataReport(CStep* pStep, const char* pszData, size_t size); |
| | | int decodeFacDataReport(CStep* pStep, const char* pszData, size_t size); |
| | | int decodePanelDataRequest(CStep* pStep, const char* pszData, size_t size); |
| | | int decodeJobDataRequest(CStep* pStep, const char* pszData, size_t size); |
| | | BOOL compareJobData(CJobDataB* pJobDataB, CJobDataS* pJobDataS); |
| | | void setProcessState(PROCESS_STATE state); |
| | | |
| | |
| | | int nRet; |
| | | CEquipment* pEq[6] = { pEFEM, pBonder1, pBonder2, pBakeCooling, |
| | | pVacuumBake, pMeasurement}; |
| | | BOOL bIomcOk[7] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}; |
| | | BOOL bIomcOk[7] = {FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}; |
| | | std::vector<std::promise<void>> promises(6); |
| | | std::vector<std::future<void>> futures; |
| | | |
| | |
| | | goto WAIT; |
| | | } |
| | | futures.push_back(promises[0].get_future()); |
| | | |
| | | /* |
| | | nRet = pEq[1]->indexerOperationModeChange(IDNEXER_OPERATION_MODE::Start, |
| | | [&](int writeCode, int retCode) -> void { |
| | | bIomcOk[1] = retCode == (int)RET::OK; |
| | |
| | | goto WAIT; |
| | | } |
| | | futures.push_back(promises[5].get_future()); |
| | | */ |
| | | |
| | | WAIT: |
| | | for (auto& f : futures) { |
| | |
| | | // å¤ç宿å½åäºå¡åï¼åæ¢å°åæ¢æå°±ç»ªç¶æ |
| | | else if (m_state == MASTERSTATE::STOPPING) { |
| | | unlock(); |
| | | Sleep(1000); |
| | | LOGI("<Master>å¼å§åæ¢å设å¤å° Stop 模å¼..."); |
| | | |
| | | std::vector<std::promise<void>> promises(6); |
| | | std::vector<std::future<void>> futures; |
| | | BOOL bIomcOk[7] = { FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }; |
| | | |
| | | int nRet; |
| | | CEquipment* pEq[6] = { pEFEM, pBonder1, pBonder2, pBakeCooling, |
| | | pVacuumBake, pMeasurement }; |
| | | |
| | | for (int i = 0; i < 1; ++i) { |
| | | nRet = pEq[i]->indexerOperationModeChange(IDNEXER_OPERATION_MODE::Stop, |
| | | [i, &promises, &bIomcOk](int writeCode, int retCode) -> void { |
| | | bIomcOk[i] = retCode == (int)RET::OK; |
| | | promises[i].set_value(); |
| | | TRACE("s000%d: ret=%d\n", i + 1, retCode); |
| | | }); |
| | | if (nRet != 0) { |
| | | LOGI("<Master>%s忢Stopç¶æåé失败", pEq[i]->getName().c_str()); |
| | | m_strLastError = pEq[i]->getName() + "忢Stopç¶æåé失败."; |
| | | bIomcOk[i] = FALSE; |
| | | promises[i].set_value(); // é¿å
wait é»å¡ |
| | | } |
| | | futures.push_back(promises[i].get_future()); |
| | | } |
| | | |
| | | for (auto& f : futures) { |
| | | f.wait(); // çå¾
ææå®æ |
| | | } |
| | | |
| | | for (int i = 0; i < 6; ++i) { |
| | | if (!bIomcOk[i]) { |
| | | bIomcOk[6] = FALSE; |
| | | LOGI("<Master>%s忢Stopç¶æå¤±è´¥", pEq[i]->getName().c_str()); |
| | | } |
| | | } |
| | | |
| | | if (!bIomcOk[6]) { |
| | | setState(MASTERSTATE::MSERROR); |
| | | continue; |
| | | } |
| | | |
| | | LOGI("<Master>ææè®¾å¤æååæ¢å° Stop 模å¼"); |
| | | setState(MASTERSTATE::READY); |
| | | continue; |
| | | } |
| | |
| | | #define STEP_ID_FETCHED_OUT_JOB_REPORT13 0x5BB |
| | | #define STEP_ID_FETCHED_OUT_JOB_REPORT14 0x5BC |
| | | #define STEP_ID_FETCHED_OUT_JOB_REPORT15 0x5BD |
| | | #define STEP_ID_JOB_DATA_REQUEST 0x5C1 |
| | | #define STEP_ID_PANEL_DATA_REQUEST 0x5D0 |
| | | #define STEP_ID_PANEL_DATA_REPORT 0x5D1 |
| | | #define STEP_ID_IN_OP_CMD_REPLY 0x5F0 |
| | |
| | | #include "Servo.h" |
| | | #include "afxdialogex.h" |
| | | #include "PageRecipe.h" |
| | | #include "SECSRuntimeManager.h" |
| | | |
| | | |
| | | // CPageRecipe å¯¹è¯æ¡ |
| | | |
| | |
| | | { |
| | | } |
| | | |
| | | void CPageRecipe::FillDataToListCtrl(const std::vector<std::string>& vecData) { |
| | | void CPageRecipe::FillDataToListCtrl(const std::vector<RecipeInfo>& vecRecipe) { |
| | | CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_PPID); |
| | | if (pListCtrl == nullptr || pListCtrl->m_hWnd == nullptr) { |
| | | return; |
| | |
| | | pListCtrl->DeleteAllItems(); |
| | | |
| | | // éåæ°æ®å¹¶æå
¥å°CListCtrlä¸ |
| | | for (int i = 0; i < static_cast<int>(vecData.size()); ++i) { |
| | | // æå
¥è¡ |
| | | pListCtrl->InsertItem(i, _T("")); |
| | | for (int i = 0; i < static_cast<int>(vecRecipe.size()); ++i) { |
| | | const RecipeInfo& recipe = vecRecipe[i]; |
| | | |
| | | // 设置 Recipe Noï¼ç¬¬1åï¼ |
| | | CString strRecipeNo; |
| | | strRecipeNo.Format(_T("%d"), i); |
| | | pListCtrl->SetItemText(i, 1, strRecipeNo); |
| | | m_listPPID.InsertItem(i, _T("")); // 第0åç©ºç½ |
| | | |
| | | // 设置 PPIDï¼ç¬¬2åï¼ |
| | | CString strPPID = CA2T(vecData[i].c_str()); |
| | | if (strPPID.CompareNoCase(_T("NULL")) == 0) { |
| | | strPPID.Empty(); |
| | | } |
| | | pListCtrl->SetItemText(i, 2, strPPID); |
| | | 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())); |
| | | } |
| | | |
| | | // è·ååæ° |
| | | int nColCount = pListCtrl->GetHeaderCtrl()->GetItemCount(); |
| | | pListCtrl->SetColumnWidth(nColCount - 1, LVSCW_AUTOSIZE_USEHEADER); |
| | | int nColCount = m_listPPID.GetHeaderCtrl()->GetItemCount(); |
| | | m_listPPID.SetColumnWidth(nColCount - 1, LVSCW_AUTOSIZE_USEHEADER); |
| | | } |
| | | |
| | | void CPageRecipe::DoDataExchange(CDataExchange* pDX) |
| | |
| | | CDialogEx::DoDataExchange(pDX); |
| | | DDX_Control(pDX, IDC_LIST_PPID, m_listPPID); |
| | | DDX_Control(pDX, IDC_EDIT_PPID, m_editPPID); |
| | | DDX_Control(pDX, IDC_EDIT_DESC, m_editDesc); |
| | | } |
| | | |
| | | |
| | | BEGIN_MESSAGE_MAP(CPageRecipe, CDialogEx) |
| | | ON_WM_SIZE() |
| | | 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_SAVE, &CPageRecipe::OnBnClickedButtonSave) |
| | | ON_BN_CLICKED(IDC_BUTTON_REFRESH, &CPageRecipe::OnBnClickedButtonRefresh) |
| | | ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_PPID, &CPageRecipe::OnLvnItemChangedListPPID) |
| | | END_MESSAGE_MAP() |
| | |
| | | |
| | | HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1); |
| | | ListView_SetImageList(pListCtrl->GetSafeHwnd(), imageList, LVSIL_SMALL); |
| | | pListCtrl->InsertColumn(0, _T(""), LVCFMT_RIGHT, 0); |
| | | pListCtrl->InsertColumn(1, _T("Recipe No"), LVCFMT_LEFT, 100); |
| | | pListCtrl->InsertColumn(2, _T("PPID"), LVCFMT_LEFT, 100); |
| | | pListCtrl->SetColumnWidth(2, LVSCW_AUTOSIZE_USEHEADER); |
| | | pListCtrl->InsertColumn(0, _T(""), LVCFMT_RIGHT, 0); // éèå |
| | | pListCtrl->InsertColumn(1, _T("No."), LVCFMT_LEFT, 80); |
| | | pListCtrl->InsertColumn(2, _T("PPID"), LVCFMT_LEFT, 120); |
| | | pListCtrl->InsertColumn(3, _T("æè¿°"), LVCFMT_LEFT, 180); |
| | | pListCtrl->InsertColumn(4, _T("å建æ¶é´"), LVCFMT_LEFT, 160); |
| | | pListCtrl->SetColumnWidth(4, LVSCW_AUTOSIZE_USEHEADER); |
| | | |
| | | // è·åæææ°æ® |
| | | auto vecData = SECSRuntimeManager::getInstance().getAllPPID(); |
| | | auto vecData = RecipeManager::getInstance().getAllRecipes(); |
| | | FillDataToListCtrl(vecData); |
| | | |
| | | return TRUE; // return TRUE unless you set the focus to a control |
| | | // å¼å¸¸: OCX 屿§é¡µåºè¿å FALSE |
| | | } |
| | | |
| | | void CPageRecipe::OnSize(UINT nType, int cx, int cy) |
| | | { |
| | | CDialogEx::OnSize(nType, cx, cy); |
| | | |
| | | if (m_listPPID.GetSafeHwnd()) { |
| | | // 左侧å表宽é«èªéåº |
| | | int margin = 10; |
| | | int buttonWidth = 80; |
| | | int buttonHeight = 30; |
| | | int buttonSpacing = 10; |
| | | |
| | | CRect rect; |
| | | GetClientRect(&rect); |
| | | |
| | | int listWidth = rect.Width() - buttonWidth - 3 * margin; |
| | | int listHeight = rect.Height() - 2 * margin; |
| | | |
| | | m_listPPID.MoveWindow(margin, margin + 80, listWidth, listHeight - 80); |
| | | |
| | | // ç¼è¾æ¡è°æ´ä½ç½®ï¼å³è¾¹å¯¹é½å表ï¼å·¦è¾¹åºå®èµ·å§ |
| | | int labelWidth = 60; |
| | | int rightEdge = rect.right - buttonWidth - 2 * margin; |
| | | |
| | | if (m_editPPID.GetSafeHwnd()) { |
| | | m_editPPID.MoveWindow(labelWidth, margin, rightEdge - labelWidth, 25); |
| | | } |
| | | if (m_editDesc.GetSafeHwnd()) { |
| | | m_editDesc.MoveWindow(labelWidth, margin + 35, rightEdge - labelWidth, 25); |
| | | } |
| | | |
| | | // æé®ç«ç´æåå¨å³ä¾§ |
| | | CWnd* buttons[] = { |
| | | GetDlgItem(IDC_BUTTON_SEARCH), |
| | | GetDlgItem(IDC_BUTTON_MODIFY), |
| | | GetDlgItem(IDC_BUTTON_DELETE), |
| | | GetDlgItem(IDC_BUTTON_DELETE_ALL), |
| | | GetDlgItem(IDC_BUTTON_REFRESH) |
| | | }; |
| | | |
| | | int y = margin; |
| | | for (auto pBtn : buttons) { |
| | | if (pBtn && pBtn->GetSafeHwnd()) { |
| | | pBtn->MoveWindow(rect.right - buttonWidth - margin, y, buttonWidth, buttonHeight); |
| | | y += buttonHeight + buttonSpacing; |
| | | } |
| | | } |
| | | |
| | | // å宽é设 |
| | | int col0 = 50; // No. |
| | | int col1 = 120; // PPID |
| | | int col3 = 160; // å建æ¶é´èªå¨å¡«å
|
| | | int col2 = listWidth - col0 - col1 - col3 - 2; // æè¿°èªå¨å¡«å
|
| | | if (col2 < 80) { |
| | | col2 = 80; |
| | | } |
| | | |
| | | m_listPPID.SetColumnWidth(1, col0); |
| | | m_listPPID.SetColumnWidth(2, col1); |
| | | m_listPPID.SetColumnWidth(3, col2); |
| | | m_listPPID.SetColumnWidth(4, col3); |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonSearch() |
| | |
| | | { |
| | | // TODO: 卿¤æ·»å æ§ä»¶éç¥å¤çç¨åºä»£ç |
| | | POSITION pos = m_listPPID.GetFirstSelectedItemPosition(); |
| | | if (!pos) return; |
| | | if (!pos) { |
| | | 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; |
| | | CString strNewPPID, strNewDesc; |
| | | m_editPPID.GetWindowText(strNewPPID); |
| | | m_listPPID.SetItemText(nSel, 2, 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("æè¿°æ´æ°å¤±è´¥")); |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonDelete() |
| | | { |
| | | // TODO: 卿¤æ·»å æ§ä»¶éç¥å¤çç¨åºä»£ç |
| | | POSITION pos = m_listPPID.GetFirstSelectedItemPosition(); |
| | | if (!pos) return; |
| | | if (!pos) { |
| | | return; |
| | | } |
| | | |
| | | int nSel = m_listPPID.GetNextSelectedItem(pos); |
| | | m_listPPID.SetItemText(nSel, 2, _T("")); |
| | | CString strPPID = m_listPPID.GetItemText(nSel, 2); |
| | | std::string ppid = CT2A(strPPID); |
| | | |
| | | if (!ppid.empty()) { |
| | | CString msg; |
| | | msg.Format(_T("ç¡®å®è¦å é¤é
æ¹ [%s] åï¼"), strPPID); |
| | | if (IDYES == AfxMessageBox(msg, MB_YESNO | MB_ICONQUESTION)) { |
| | | if (RecipeManager::getInstance().deleteRecipeByPPID(ppid)) { |
| | | m_listPPID.DeleteItem(nSel); |
| | | } |
| | | else { |
| | | AfxMessageBox(_T("å é¤å¤±è´¥")); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonDeleteAll() |
| | | { |
| | | // TODO: 卿¤æ·»å æ§ä»¶éç¥å¤çç¨åºä»£ç |
| | | int nCount = m_listPPID.GetItemCount(); |
| | | for (int i = 0; i < nCount; ++i) { |
| | | m_listPPID.SetItemText(i, 2, _T("")); |
| | | if (IDYES != AfxMessageBox(_T("ç¡®å®è¦å é¤å
¨é¨é
æ¹è®°å½åï¼"), MB_YESNO | MB_ICONWARNING)) { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonSave() |
| | | { |
| | | // TODO: 卿¤æ·»å æ§ä»¶éç¥å¤çç¨åºä»£ç |
| | | std::vector<std::string> vecPPID; |
| | | // è·åææ PPID |
| | | int nCount = m_listPPID.GetItemCount(); |
| | | for (int i = 0; i < nCount; ++i) { |
| | | CString str = m_listPPID.GetItemText(i, 2); |
| | | vecPPID.emplace_back(CT2A(str)); |
| | | CString strPPID = m_listPPID.GetItemText(i, 2); |
| | | std::string ppid = CT2A(strPPID); |
| | | if (!ppid.empty()) { |
| | | RecipeManager::getInstance().deleteRecipeByPPID(ppid); |
| | | } |
| | | } |
| | | SECSRuntimeManager::getInstance().setAllPPID(vecPPID); |
| | | |
| | | // æ¸
空å表æ¾ç¤º |
| | | m_listPPID.DeleteAllItems(); |
| | | } |
| | | |
| | | void CPageRecipe::OnBnClickedButtonRefresh() |
| | | { |
| | | // TODO: 卿¤æ·»å æ§ä»¶éç¥å¤çç¨åºä»£ç |
| | | auto vecData = SECSRuntimeManager::getInstance().getAllPPID(); |
| | | auto vecData = RecipeManager::getInstance().getAllRecipes(); |
| | | FillDataToListCtrl(vecData); |
| | | } |
| | | |
| | |
| | | int nItem = pNMLV->iItem; |
| | | CString strPPID = m_listPPID.GetItemText(nItem, 2); |
| | | m_editPPID.SetWindowText(strPPID); |
| | | |
| | | CString strDesc = m_listPPID.GetItemText(nItem, 3); |
| | | m_editDesc.SetWindowText(strDesc); |
| | | } |
| | | } |
| | |
| | | #pragma once |
| | | #include "afxdialogex.h" |
| | | //#include "ListCtrlEx.h" |
| | | |
| | | #include "RecipeManager.h" |
| | | |
| | | // CPageRecipe å¯¹è¯æ¡ |
| | | |
| | |
| | | virtual ~CPageRecipe(); |
| | | |
| | | private: |
| | | void FillDataToListCtrl(const std::vector<std::string>& vecData); |
| | | void FillDataToListCtrl(const std::vector<RecipeInfo>& vecRecipe); |
| | | |
| | | // å¯¹è¯æ¡æ°æ® |
| | | #ifdef AFX_DESIGN_TIME |
| | |
| | | protected: |
| | | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV æ¯æ |
| | | virtual BOOL OnInitDialog(); |
| | | afx_msg void OnSize(UINT nType, int cx, int cy); |
| | | afx_msg void OnBnClickedButtonSearch(); |
| | | afx_msg void OnBnClickedButtonModify(); |
| | | afx_msg void OnBnClickedButtonDelete(); |
| | | afx_msg void OnBnClickedButtonDeleteAll(); |
| | | afx_msg void OnBnClickedButtonSave(); |
| | | afx_msg void OnBnClickedButtonRefresh(); |
| | | afx_msg void OnLvnItemChangedListPPID(NMHDR* pNMHDR, LRESULT* pResult); |
| | | DECLARE_MESSAGE_MAP() |
| | |
| | | private: |
| | | CListCtrl m_listPPID; |
| | | CEdit m_editPPID; |
| | | CEdit m_editDesc; |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // RecipeDeviceBindDlg.cpp: å®ç°æä»¶ |
| | | // |
| | | |
| | | #include "stdafx.h" |
| | | #include "Servo.h" |
| | | #include "afxdialogex.h" |
| | | #include "RecipeDeviceBindDlg.h" |
| | | |
| | | |
| | | // CRecipeDeviceBindDlg å¯¹è¯æ¡ |
| | | |
| | | IMPLEMENT_DYNAMIC(CRecipeDeviceBindDlg, CDialogEx) |
| | | |
| | | CRecipeDeviceBindDlg::CRecipeDeviceBindDlg(CWnd* pParent /*=nullptr*/) |
| | | : CDialogEx(IDD_DIALOG_RECIPE_DEVICE_BIND, pParent) |
| | | { |
| | | |
| | | } |
| | | |
| | | CRecipeDeviceBindDlg::~CRecipeDeviceBindDlg() |
| | | { |
| | | } |
| | | |
| | | void CRecipeDeviceBindDlg::DoDataExchange(CDataExchange* pDX) |
| | | { |
| | | CDialogEx::DoDataExchange(pDX); |
| | | } |
| | | |
| | | |
| | | BEGIN_MESSAGE_MAP(CRecipeDeviceBindDlg, CDialogEx) |
| | | END_MESSAGE_MAP() |
| | | |
| | | |
| | | // CRecipeDeviceBindDlg æ¶æ¯å¤çç¨åº |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #pragma once |
| | | #include "afxdialogex.h" |
| | | |
| | | |
| | | // CRecipeDeviceBindDlg å¯¹è¯æ¡ |
| | | |
| | | class CRecipeDeviceBindDlg : public CDialogEx |
| | | { |
| | | DECLARE_DYNAMIC(CRecipeDeviceBindDlg) |
| | | |
| | | public: |
| | | CRecipeDeviceBindDlg(CWnd* pParent = nullptr); // æ åæé 彿° |
| | | virtual ~CRecipeDeviceBindDlg(); |
| | | |
| | | // å¯¹è¯æ¡æ°æ® |
| | | #ifdef AFX_DESIGN_TIME |
| | | enum { IDD = IDD_DIALOG_RECIPE_DEVICE_BIND }; |
| | | #endif |
| | | |
| | | protected: |
| | | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV æ¯æ |
| | | |
| | | DECLARE_MESSAGE_MAP() |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #include "stdafx.h" |
| | | #include "RecipeManager.h" |
| | | #include <sstream> |
| | | #include <iomanip> |
| | | #include <fstream> |
| | | #include <iostream> |
| | | |
| | | std::recursive_mutex RecipeManager::m_mutex; |
| | | |
| | | RecipeManager& RecipeManager::getInstance() { |
| | | static RecipeManager instance; |
| | | return instance; |
| | | } |
| | | |
| | | RecipeManager::RecipeManager() { |
| | | m_pDB = new BL::SQLiteDatabase(); |
| | | } |
| | | |
| | | RecipeManager::~RecipeManager() { |
| | | if (m_pDB) { |
| | | delete m_pDB; |
| | | m_pDB = nullptr; |
| | | } |
| | | } |
| | | |
| | | bool RecipeManager::initRecipeTable() { |
| | | char szPath[MAX_PATH]; |
| | | GetModuleFileNameA(NULL, szPath, MAX_PATH); |
| | | std::string exePath(szPath); |
| | | std::string dbDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\DB"; |
| | | CreateDirectoryA(dbDir.c_str(), NULL); |
| | | |
| | | std::string dbPath = dbDir + "\\RecipeManager.db"; |
| | | if (!m_pDB->connect(dbPath, true)) { |
| | | return false; |
| | | } |
| | | |
| | | // å¯ç¨ SQLite çå¤é®çº¦ææ¯æ |
| | | if (!m_pDB->executeQuery("PRAGMA foreign_keys = ON;")) { |
| | | std::cerr << "Failed to enable foreign keys." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | const std::string createRecipeTable = R"( |
| | | CREATE TABLE IF NOT EXISTS recipes ( |
| | | ppid TEXT PRIMARY KEY NOT NULL, |
| | | description TEXT, |
| | | create_time TEXT DEFAULT (datetime('now', 'localtime')) |
| | | ); |
| | | )"; |
| | | |
| | | const std::string createDeviceTable = R"( |
| | | CREATE TABLE IF NOT EXISTS recipe_devices ( |
| | | id INTEGER PRIMARY KEY AUTOINCREMENT, |
| | | ppid TEXT NOT NULL, |
| | | device_id INTEGER NOT NULL, |
| | | device_name TEXT NOT NULL, |
| | | recipe_id INTEGER NOT NULL, |
| | | FOREIGN KEY(ppid) REFERENCES recipes(ppid) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | UNIQUE (ppid, device_id), |
| | | UNIQUE (ppid, device_name) |
| | | ); |
| | | )"; |
| | | |
| | | return m_pDB->executeQuery(createRecipeTable) |
| | | && m_pDB->executeQuery(createDeviceTable); |
| | | } |
| | | |
| | | void RecipeManager::termRecipeTable() { |
| | | if (!m_pDB) { |
| | | return; |
| | | } |
| | | |
| | | m_pDB->disconnect(); |
| | | } |
| | | |
| | | bool RecipeManager::destroyRecipeTable() { |
| | | if (!m_pDB) { |
| | | return false; |
| | | } |
| | | |
| | | return m_pDB->executeQuery("DROP TABLE IF EXISTS recipe_devices;") && m_pDB->executeQuery("DROP TABLE IF EXISTS recipes;"); |
| | | } |
| | | |
| | | bool RecipeManager::ppidExists(const std::string& ppid) { |
| | | std::ostringstream oss; |
| | | oss << "SELECT COUNT(*) FROM recipes WHERE ppid = '" << ppid << "';"; |
| | | auto result = m_pDB->fetchResults(oss.str()); |
| | | return (!result.empty() && !result[0].empty() && result[0][0] != "0"); |
| | | } |
| | | |
| | | bool RecipeManager::deviceExists(const std::string& ppid, int nDeviceID) { |
| | | std::ostringstream oss; |
| | | oss << "SELECT COUNT(*) FROM recipe_devices WHERE ppid = '" << ppid |
| | | << "' AND device_id = " << nDeviceID << ";"; |
| | | auto result = m_pDB->fetchResults(oss.str()); |
| | | return (!result.empty() && !result[0].empty() && result[0][0] != "0"); |
| | | } |
| | | |
| | | bool RecipeManager::addRecipe(const RecipeInfo& recipe) { |
| | | if (!m_pDB || recipe.strPPID.empty() || recipe.vecDeviceList.empty()) { |
| | | std::cerr << "[AddRecipe] Invalid input." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | std::string strTime = recipe.strCreateTime; |
| | | if (strTime.empty()) { |
| | | std::time_t now = std::time(nullptr); |
| | | std::tm tm_now = {}; |
| | | localtime_s(&tm_now, &now); |
| | | std::stringstream ss; |
| | | ss << std::put_time(&tm_now, "%Y-%m-%d %H:%M:%S"); |
| | | strTime = ss.str(); |
| | | } |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | |
| | | // å¼å§äºå¡ |
| | | m_pDB->executeQuery("BEGIN TRANSACTION;"); |
| | | |
| | | std::ostringstream oss; |
| | | oss << "INSERT OR REPLACE INTO recipes (ppid, description, create_time) VALUES ('" |
| | | << recipe.strPPID << "', '" |
| | | << recipe.strDescription << "', '" |
| | | << strTime << "');"; |
| | | |
| | | if (!m_pDB->executeQuery(oss.str())) { |
| | | std::cerr << "[AddRecipe] Failed to insert recipe: " << recipe.strPPID << std::endl; |
| | | m_pDB->executeQuery("ROLLBACK;"); |
| | | return false; |
| | | } |
| | | |
| | | for (const auto& device : recipe.vecDeviceList) { |
| | | std::ostringstream devSql; |
| | | devSql << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id) VALUES ('" |
| | | << recipe.strPPID << "', " |
| | | << device.nDeviceID << ", '" |
| | | << device.strDeviceName << "', " |
| | | << device.nRecipeID << ");"; |
| | | |
| | | if (!m_pDB->executeQuery(devSql.str())) { |
| | | std::cerr << "[AddRecipe] Failed to insert device mapping: " << device.nDeviceID << std::endl; |
| | | m_pDB->executeQuery("ROLLBACK;"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // æäº¤äºå¡ |
| | | m_pDB->executeQuery("COMMIT;"); |
| | | return true; |
| | | } |
| | | |
| | | bool RecipeManager::addRecipeDevice(const std::string& ppid, const DeviceRecipe& device) { |
| | | if (!m_pDB || ppid.empty() || device.nDeviceID <= 0 || device.nRecipeID <= 0) { |
| | | std::cerr << "[addRecipeDevice] Invalid input." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | // æ£æ¥ ppid æ¯å¦åå¨ |
| | | if (!ppidExists(ppid)) { |
| | | std::cerr << "[addRecipeDevice] PPID does not exist: " << ppid << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | // æå
¥è®¾å¤è®°å½ |
| | | std::ostringstream oss; |
| | | oss << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id) VALUES ('" |
| | | << ppid << "', " |
| | | << device.nDeviceID << ", '" |
| | | << device.strDeviceName << "', " |
| | | << device.nRecipeID << ");"; |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | return m_pDB->executeQuery(oss.str()); |
| | | } |
| | | |
| | | bool RecipeManager::deleteRecipeDeviceByID(const std::string& ppid, int nDeviceID) { |
| | | if (!m_pDB || ppid.empty() || nDeviceID <= 0) { |
| | | std::cerr << "[deleteRecipeDeviceByID] Invalid input." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | std::ostringstream oss; |
| | | oss << "DELETE FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";"; |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | return m_pDB->executeQuery(oss.str()); |
| | | } |
| | | |
| | | bool RecipeManager::deleteRecipeDeviceByName(const std::string& ppid, const std::string& strDeviceName) { |
| | | if (!m_pDB || ppid.empty() || strDeviceName.empty()) { |
| | | std::cerr << "[deleteRecipeDeviceByName] Invalid input." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | std::ostringstream oss; |
| | | oss << "DELETE FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';"; |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | return m_pDB->executeQuery(oss.str()); |
| | | } |
| | | |
| | | std::vector<RecipeInfo> RecipeManager::getAllRecipes() { |
| | | if (!m_pDB) { |
| | | return {}; |
| | | } |
| | | |
| | | std::vector<RecipeInfo> recipes; |
| | | auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes;"); |
| | | |
| | | for (const auto& row : rows) { |
| | | RecipeInfo info; |
| | | info.strPPID = row[0]; |
| | | info.strDescription = row[1]; |
| | | info.strCreateTime = row[2]; |
| | | |
| | | std::ostringstream devQuery; |
| | | devQuery << "SELECT device_id, device_name, recipe_id FROM recipe_devices WHERE ppid = '" << info.strPPID << "';"; |
| | | auto devs = m_pDB->fetchResults(devQuery.str()); |
| | | |
| | | for (const auto& dev : devs) { |
| | | DeviceRecipe dr; |
| | | dr.strPPID = info.strPPID; |
| | | try { |
| | | dr.nDeviceID = std::stoi(dev[0]); |
| | | dr.strDeviceName = dev[1]; |
| | | dr.nRecipeID = std::stoi(dev[2]); |
| | | } |
| | | catch (...) { |
| | | std::cerr << "Invalid data in recipe_devices for PPID: " << info.strPPID << std::endl; |
| | | continue; |
| | | } |
| | | info.vecDeviceList.push_back(dr); |
| | | } |
| | | recipes.push_back(info); |
| | | } |
| | | |
| | | return recipes; |
| | | } |
| | | |
| | | RecipeInfo RecipeManager::getRecipeByPPID(const std::string& ppid) { |
| | | RecipeInfo info; |
| | | auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes WHERE ppid = '" + ppid + "';"); |
| | | if (rows.empty()) { |
| | | return info; |
| | | } |
| | | |
| | | info.strPPID = rows[0][0]; |
| | | info.strDescription = rows[0][1]; |
| | | info.strCreateTime = rows[0][2]; |
| | | |
| | | 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]; |
| | | dr.nRecipeID = std::stoi(dev[2]); |
| | | } |
| | | catch (...) { |
| | | std::cerr << "Invalid data in recipe_devices for PPID: " << ppid << std::endl; |
| | | continue; |
| | | } |
| | | info.vecDeviceList.push_back(dr); |
| | | } |
| | | return info; |
| | | } |
| | | |
| | | int RecipeManager::getDeviceRecipeIDByID(const std::string& ppid, int nDeviceID) { |
| | | if (!m_pDB || ppid.empty() || nDeviceID <= 0) { |
| | | return -1; |
| | | } |
| | | |
| | | std::ostringstream query; |
| | | query << "SELECT recipe_id FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";"; |
| | | |
| | | auto result = m_pDB->fetchResults(query.str()); |
| | | if (!result.empty() && !result[0].empty()) { |
| | | try { |
| | | return std::stoi(result[0][0]); |
| | | } |
| | | catch (...) { |
| | | return -1; |
| | | } |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | int RecipeManager::getDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName) { |
| | | if (!m_pDB || ppid.empty() || strDeviceName.empty()) { |
| | | return -1; |
| | | } |
| | | |
| | | std::ostringstream query; |
| | | query << "SELECT recipe_id FROM recipe_devices WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';"; |
| | | |
| | | auto result = m_pDB->fetchResults(query.str()); |
| | | if (!result.empty() && !result[0].empty()) { |
| | | try { |
| | | return std::stoi(result[0][0]); |
| | | } |
| | | catch (...) { |
| | | return -1; |
| | | } |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | bool RecipeManager::deleteRecipeByPPID(const std::string& ppid) { |
| | | if (!m_pDB) { |
| | | return false; |
| | | } |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | return m_pDB->executeQuery("DELETE FROM recipes WHERE ppid = '" + ppid + "';"); |
| | | } |
| | | |
| | | bool RecipeManager::updateRecipe(const RecipeInfo& recipe) { |
| | | if (!m_pDB) { |
| | | return false; |
| | | } |
| | | |
| | | if (recipe.strPPID.empty()) { |
| | | std::cerr << "Recipe PPID cannot be empty." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | deleteRecipeByPPID(recipe.strPPID); |
| | | return addRecipe(recipe); |
| | | } |
| | | |
| | | bool RecipeManager::updatePPID(const std::string& oldPPID, const std::string& newPPID) { |
| | | if (!m_pDB || oldPPID.empty() || newPPID.empty()) { |
| | | std::cerr << "[updatePPID] Invalid input." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | |
| | | // æ£æ¥æ¯å¦å·²ç»åå¨ç¸åç newPPID |
| | | auto check = m_pDB->fetchResults("SELECT COUNT(*) FROM recipes WHERE ppid = '" + newPPID + "';"); |
| | | if (!check.empty() && !check[0].empty() && check[0][0] != "0") { |
| | | std::cerr << "[updatePPID] New PPID already exists: " << newPPID << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | m_pDB->executeQuery("BEGIN TRANSACTION;"); |
| | | |
| | | std::ostringstream sql; |
| | | sql << "UPDATE recipes SET ppid = '" << newPPID << "' WHERE ppid = '" << oldPPID << "';"; |
| | | if (!m_pDB->executeQuery(sql.str())) { |
| | | std::cerr << "[updatePPID] Failed to update recipes table." << std::endl; |
| | | m_pDB->executeQuery("ROLLBACK;"); |
| | | return false; |
| | | } |
| | | |
| | | m_pDB->executeQuery("COMMIT;"); |
| | | return true; |
| | | } |
| | | |
| | | bool RecipeManager::updateDescription(const std::string& ppid, const std::string& newDescription) { |
| | | if (!m_pDB || ppid.empty()) { |
| | | std::cerr << "[updateRecipeDescription] Invalid input." << std::endl; |
| | | return false; |
| | | } |
| | | |
| | | std::ostringstream oss; |
| | | oss << "UPDATE recipes SET description = '" << newDescription << "' WHERE ppid = '" << ppid << "';"; |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | return m_pDB->executeQuery(oss.str()); |
| | | } |
| | | |
| | | bool RecipeManager::updateDeviceRecipeIDByID(const std::string& ppid, int nDeviceID, int nNewRecipeID) { |
| | | if (!m_pDB || ppid.empty() || nDeviceID <= 0 || nNewRecipeID <= 0) { |
| | | return false; |
| | | } |
| | | |
| | | std::ostringstream query; |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | query << "UPDATE recipe_devices SET recipe_id = " << nNewRecipeID |
| | | << " WHERE ppid = '" << ppid << "' AND device_id = " << nDeviceID << ";"; |
| | | return m_pDB->executeQuery(query.str()); |
| | | } |
| | | |
| | | bool RecipeManager::updateDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName, int nNewRecipeID) { |
| | | if (!m_pDB || ppid.empty() || strDeviceName.empty() || nNewRecipeID <= 0) { |
| | | return false; |
| | | } |
| | | std::ostringstream query; |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | query << "UPDATE recipe_devices SET recipe_id = " << nNewRecipeID |
| | | << " WHERE ppid = '" << ppid << "' AND device_name = '" << strDeviceName << "';"; |
| | | return m_pDB->executeQuery(query.str()); |
| | | } |
| | | |
| | | void RecipeManager::insertMockData() { |
| | | if (!m_pDB) { |
| | | return; |
| | | } |
| | | |
| | | RecipeInfo recipe; |
| | | recipe.strPPID = "P1001"; |
| | | recipe.strDescription = "Main Board Burn-in"; |
| | | |
| | | recipe.vecDeviceList = { |
| | | {1, 101, "P1001","Burner A"}, |
| | | {2, 102, "P1001", "Burner B"} |
| | | }; |
| | | |
| | | addRecipe(recipe); |
| | | } |
| | | |
| | | bool RecipeManager::readRecipeFile(const std::string& filename) { |
| | | if (!m_pDB) { |
| | | return false; |
| | | } |
| | | |
| | | std::ifstream file(filename); |
| | | if (!file.is_open()) { |
| | | return false; |
| | | } |
| | | |
| | | std::unordered_map<std::string, RecipeInfo> recipeMap; |
| | | std::string line; |
| | | std::getline(file, line); // skip header |
| | | |
| | | while (std::getline(file, line)) { |
| | | std::stringstream ss(line); |
| | | std::string cell; |
| | | std::string ppid, description, createTime; |
| | | DeviceRecipe dev; |
| | | |
| | | std::getline(ss, ppid, ','); |
| | | std::getline(ss, cell, ','); |
| | | try { dev.nDeviceID = std::stoi(cell); } |
| | | catch (...) { continue; } |
| | | std::getline(ss, dev.strDeviceName, ','); |
| | | std::getline(ss, cell, ','); |
| | | try { dev.nRecipeID = std::stoi(cell); } |
| | | catch (...) { continue; } |
| | | std::getline(ss, description, ','); |
| | | std::getline(ss, createTime, ','); |
| | | |
| | | dev.strPPID = ppid; |
| | | auto& recipe = recipeMap[ppid]; |
| | | recipe.strPPID = ppid; |
| | | recipe.strDescription = description; |
| | | recipe.strCreateTime = createTime; |
| | | recipe.vecDeviceList.push_back(dev); |
| | | } |
| | | |
| | | for (const auto& pair : recipeMap) { |
| | | if (!updateRecipe(pair.second)) { |
| | | std::cerr << "Failed to update recipe from file: " << pair.first << std::endl; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool RecipeManager::saveRecipeFile(const std::string& filename) { |
| | | if (!m_pDB) { |
| | | return false; |
| | | } |
| | | |
| | | std::ofstream file(filename); |
| | | if (!file.is_open()) { |
| | | return false; |
| | | } |
| | | |
| | | file << "PPID,DeviceID,DeviceName,RecipeID,Description,CreateTime\n"; |
| | | auto recipes = getAllRecipes(); |
| | | for (const auto& recipe : recipes) { |
| | | for (const auto& dev : recipe.vecDeviceList) { |
| | | file << recipe.strPPID << "," |
| | | << dev.nDeviceID << "," |
| | | << dev.strDeviceName << "," |
| | | << dev.nRecipeID << "," |
| | | << recipe.strDescription << "," |
| | | << recipe.strCreateTime << "\n"; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | #ifndef RECIPE_MANAGER_H |
| | | #define RECIPE_MANAGER_H |
| | | |
| | | #include <string> |
| | | #include <vector> |
| | | #include <mutex> |
| | | #include <unordered_map> |
| | | #include "Database.h" |
| | | |
| | | // å个设å¤é
æ¹æ å°ä¿¡æ¯ |
| | | struct DeviceRecipe { |
| | | int nDeviceID; // 设å¤ID |
| | | int nRecipeID; // 该设å¤å¯¹åºçåé
æ¹ID |
| | | std::string strPPID; // é
æ¹IDï¼ä¸»é®ï¼ |
| | | std::string strDeviceName; // 设å¤åç§° |
| | | }; |
| | | |
| | | // é
æ¹ä¿¡æ¯ |
| | | struct RecipeInfo { |
| | | std::string strPPID; // é
æ¹ID |
| | | std::string strDescription; // é
æ¹æè¿° |
| | | std::string strCreateTime; // å建æ¶é´ |
| | | std::vector<DeviceRecipe> vecDeviceList; // å
³èç设å¤ä¿¡æ¯å表 |
| | | }; |
| | | |
| | | using RecipeMap = std::unordered_map<std::string, RecipeInfo>; // æ PPID æ å°çé
æ¹è¡¨ |
| | | |
| | | class RecipeManager { |
| | | public: |
| | | // è·ååä¾ |
| | | static RecipeManager& getInstance(); |
| | | |
| | | // åå§åé
æ¹æ°æ®åº |
| | | bool initRecipeTable(); |
| | | |
| | | // éæ¯è¡¨æå
³éè¿æ¥ |
| | | void termRecipeTable(); |
| | | bool destroyRecipeTable(); |
| | | |
| | | // æ£æ¥ PPID æ¯å¦åå¨ |
| | | bool ppidExists(const std::string& ppid); |
| | | |
| | | // æ£æ¥è®¾å¤æ¯å¦åå¨äºæå® PPID çé
æ¹ä¸ |
| | | bool deviceExists(const std::string& ppid, int nDeviceID); |
| | | |
| | | // æ·»å ä¸ä¸ªé
æ¹åå
¶è®¾å¤æ å° |
| | | bool addRecipe(const RecipeInfo& recipe); |
| | | |
| | | // æ·»å 设å¤å°æå®é
æ¹ |
| | | bool addRecipeDevice(const std::string& ppid, const DeviceRecipe& device); |
| | | |
| | | // å é¤æå® PPID ç设å¤é
æ¹ |
| | | bool deleteRecipeDeviceByID(const std::string& ppid, int nDeviceID); |
| | | |
| | | // å é¤æå® PPID ç设å¤é
æ¹ï¼éè¿è®¾å¤åç§°ï¼ |
| | | bool deleteRecipeDeviceByName(const std::string& ppid, const std::string& strDeviceName); |
| | | |
| | | // æ¥è¯¢ææé
æ¹ |
| | | std::vector<RecipeInfo> getAllRecipes(); |
| | | |
| | | // æ PPID æ¥è¯¢é
æ¹ |
| | | RecipeInfo getRecipeByPPID(const std::string& ppid); |
| | | |
| | | // æ ¹æ® PPID å设å¤ID è·å设å¤é
æ¹ID |
| | | int getDeviceRecipeIDByID(const std::string& ppid, int nDeviceID); |
| | | |
| | | // æ ¹æ® PPID å设å¤åç§° è·å设å¤é
æ¹ID |
| | | int getDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName); |
| | | |
| | | // å é¤æå® PPID çé
æ¹ |
| | | bool deleteRecipeByPPID(const std::string& ppid); |
| | | |
| | | // æ´æ°æå® PPID çé
æ¹ |
| | | bool updateRecipe(const RecipeInfo& recipe); |
| | | |
| | | // æ´æ° PPIDï¼éè¿æ§ PPID åæ° PPIDï¼ |
| | | bool updatePPID(const std::string& oldPPID, const std::string& newPPID); |
| | | |
| | | // æ´æ°é
æ¹æè¿°ï¼éè¿ PPIDï¼ |
| | | bool updateDescription(const std::string& ppid, const std::string& newDescription); |
| | | |
| | | // æ´æ°è®¾å¤é
æ¹IDï¼éè¿ PPID å设å¤IDï¼ |
| | | bool updateDeviceRecipeIDByID(const std::string& ppid, int nDeviceID, int nNewRecipeID); |
| | | |
| | | // æ´æ°è®¾å¤é
æ¹IDï¼éè¿ PPID å设å¤åç§°ï¼ |
| | | bool updateDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName, int nNewRecipeID); |
| | | |
| | | // 模ææå
¥æ°æ®ï¼æµè¯ç¨ï¼ |
| | | void insertMockData(); |
| | | |
| | | // 读åé
æ¹æä»¶ï¼CSV æ JSONï¼ |
| | | bool readRecipeFile(const std::string& filename); |
| | | |
| | | // ä¿åé
æ¹å°æä»¶ |
| | | bool saveRecipeFile(const std::string& filename); |
| | | |
| | | private: |
| | | RecipeManager(); |
| | | ~RecipeManager(); |
| | | |
| | | RecipeManager(const RecipeManager&) = delete; |
| | | RecipeManager& operator=(const RecipeManager&) = delete; |
| | | |
| | | private: |
| | | BL::Database* m_pDB; |
| | | static std::recursive_mutex m_mutex; |
| | | }; |
| | | |
| | | #endif // RECIPE_MANAGER_H |
| | |
| | | #include "ServoDlg.h" |
| | | #include "ServoGraph.h" |
| | | #include "AlarmManager.h" |
| | | #include "SECSRuntimeManager.h" |
| | | #include "TransferManager.h" |
| | | #include "SystemLogManager.h" |
| | | #include "UserManager.h" |
| | | #include "RecipeManager.h" |
| | | #include "VerticalLine.h" |
| | | #include "HorizontalLine.h" |
| | | #include "EqsGraphWnd.h" |
| | |
| | | AfxMessageBox(errorMsg, MB_ICONERROR); |
| | | return FALSE; |
| | | } |
| | | AlarmManager::getInstance().insertMockData(); |
| | | |
| | | // åå§åSECSè¿è¡è®¾ç½®ç®¡çåº |
| | | try { |
| | | if (!SECSRuntimeManager::getInstance().initRuntimeSetting()) { |
| | | AfxMessageBox("åå§åSECSè¿è¡è®¾ç½®å¤±è´¥ï¼"); |
| | | return FALSE; |
| | | } |
| | | } |
| | | catch (const std::exception& ex) { |
| | | CString errorMsg; |
| | | errorMsg.Format(_T("åå§åSECSè¿è¡è®¾ç½®å¤±è´¥ï¼%s"), CString(ex.what())); |
| | | AfxMessageBox(errorMsg, MB_ICONERROR); |
| | | return FALSE; |
| | | } |
| | | |
| | | // åå§åæ¬è¿è®°å½ç®¡çåº |
| | | try { |
| | |
| | | |
| | | // åå§åè¿è¡æ¥å¿ç®¡çåº |
| | | try { |
| | | if (!SystemLogManager::getInstance().initializeLogTable()) { |
| | | if (!SystemLogManager::getInstance().initSystemLogTable()) { |
| | | AfxMessageBox("åå§åè¿è¡æ¥å¿ç®¡çåºå¤±è´¥ï¼"); |
| | | return FALSE; |
| | | } |
| | |
| | | return FALSE; |
| | | } |
| | | |
| | | // åå§åé
æ¹ç®¡çåº |
| | | try { |
| | | if (!RecipeManager::getInstance().initRecipeTable()) { |
| | | AfxMessageBox("åå§åé
æ¹ç®¡çåºå¤±è´¥ï¼"); |
| | | return FALSE; |
| | | } |
| | | } |
| | | catch (const std::exception& ex) { |
| | | CString errorMsg; |
| | | errorMsg.Format(_T("åå§åé
æ¹ç®¡çåºå¤±è´¥ï¼%s"), CString(ex.what())); |
| | | AfxMessageBox(errorMsg, MB_ICONERROR); |
| | | return FALSE; |
| | | } |
| | | |
| | | |
| | | CServoDlg dlg; |
| | |
| | | // 鿝æ¥è¦è¡¨ |
| | | AlarmManager::getInstance().termAlarmTable(); |
| | | |
| | | // 鿝SECSè¿è¡è®¾ç½®ç®¡çåº |
| | | SECSRuntimeManager::getInstance().termRuntimeSetting(); |
| | | |
| | | // 鿝æ¬è¿è®°å½ç®¡çåº |
| | | TransferManager::getInstance().termTransferTable(); |
| | | |
| | | // 鿝è¿è¡æ¥å¿ |
| | | SystemLogManager::getInstance().termSystemLogTable(); |
| | | |
| | | // éæ¯ç¨æ·è¡¨ |
| | | #if !defined(_DEBUG) |
| | | // æ¸
é¤ UserManager çæ æä½æ£æµ |
| | | UserManager::getInstance().terminateIdleDetection(); |
| | | KillTimer(1); |
| | | #endif |
| | | |
| | | // 鿝é
æ¹è¡¨ |
| | | RecipeManager::getInstance().termRecipeTable(); |
| | | |
| | | return CWinApp::ExitInstance(); |
| | | } |
| | | |
| | |
| | | <ClInclude Include="PageTransferLog.h" /> |
| | | <ClInclude Include="PortConfigurationDlg.h" /> |
| | | <ClInclude Include="ProductionLogManager.h" /> |
| | | <ClInclude Include="RecipeDeviceBindDlg.h" /> |
| | | <ClInclude Include="RecipeManager.h" /> |
| | | <ClInclude Include="Resource.h" /> |
| | | <ClInclude Include="SECSRuntimeManager.h" /> |
| | | <ClInclude Include="SecsTestDlg.h" /> |
| | |
| | | <ClCompile Include="PageTransferLog.cpp" /> |
| | | <ClCompile Include="PortConfigurationDlg.cpp" /> |
| | | <ClCompile Include="ProductionLogManager.cpp" /> |
| | | <ClCompile Include="RecipeDeviceBindDlg.cpp" /> |
| | | <ClCompile Include="RecipeManager.cpp" /> |
| | | <ClCompile Include="SECSRuntimeManager.cpp" /> |
| | | <ClCompile Include="SecsTestDlg.cpp" /> |
| | | <ClCompile Include="Servo.cpp" /> |
| | |
| | | <ClCompile Include="SystemLogManager.cpp" /> |
| | | <ClCompile Include="UserManager.cpp" /> |
| | | <ClCompile Include="InputDialog.cpp" /> |
| | | <ClCompile Include="RecipeManager.cpp" /> |
| | | <ClCompile Include="RecipeDeviceBindDlg.cpp" /> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <ClInclude Include="AlarmManager.h" /> |
| | |
| | | <ClInclude Include="UserManager.h" /> |
| | | <ClInclude Include="SystemLogManager.h" /> |
| | | <ClInclude Include="InputDialog.h" /> |
| | | <ClInclude Include="RecipeManager.h" /> |
| | | <ClInclude Include="RecipeDeviceBindDlg.h" /> |
| | | </ItemGroup> |
| | | <ItemGroup> |
| | | <ResourceCompile Include="Servo.rc" /> |
| | |
| | | <RemoteDebuggerCommand>\\DESKTOP-IODBVIQ\Servo\Debug\Servo.exe</RemoteDebuggerCommand> |
| | | <RemoteDebuggerWorkingDirectory>\\DESKTOP-IODBVIQ\Servo\Debug\</RemoteDebuggerWorkingDirectory> |
| | | <RemoteDebuggerServerName>DESKTOP-IODBVIQ</RemoteDebuggerServerName> |
| | | <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> |
| | | <DebuggerFlavor>WindowsRemoteDebugger</DebuggerFlavor> |
| | | </PropertyGroup> |
| | | </Project> |
| | |
| | | } |
| | | |
| | | // åå§åæ¥å¿è¡¨ |
| | | bool SystemLogManager::initializeLogTable() { |
| | | bool SystemLogManager::initSystemLogTable() { |
| | | // è·å坿§è¡æä»¶è·¯å¾ |
| | | char szPath[MAX_PATH]; |
| | | GetModuleFileName(NULL, szPath, MAX_PATH); |
| | |
| | | } |
| | | |
| | | // æé æ°æ®åºè·¯å¾ |
| | | std::string dbPath = dbDir + "\\SystemLog.db"; |
| | | std::string dbPath = dbDir + "\\SystemLogManager.db"; |
| | | |
| | | // è¿æ¥æ°æ®åº |
| | | if (!m_pDB->connect(dbPath, true)) { |
| | |
| | | return m_pDB->executeQuery(createTableQuery); |
| | | } |
| | | |
| | | // ç»æ¢æ°æ®åºè¿æ¥ |
| | | void SystemLogManager::termSystemLogTable() { |
| | | if (!m_pDB) { |
| | | return; |
| | | } |
| | | |
| | | m_pDB->disconnect(); |
| | | } |
| | | |
| | | // æ·»å æ¥å¿ï¼ä½¿ç¨å½åç¨æ·ï¼ |
| | | bool SystemLogManager::log(LogType logType, const std::string& event) { |
| | | if (!m_pDB) { |
| | |
| | | static SystemLogManager& getInstance(); |
| | | |
| | | // åå§åæ¥å¿è¡¨ |
| | | bool initializeLogTable(); |
| | | bool initSystemLogTable(); |
| | | |
| | | // ç»æ¢æ°æ®åºè¿æ¥ |
| | | void termSystemLogTable(); |
| | | |
| | | // æ·»å æ¥å¿ |
| | | bool log(LogType logType, const std::string& event); |
| | |
| | | #include <sstream> |
| | | |
| | | const std::string SESSION_FILE = R"(session.dat)"; |
| | | const std::string DATABASE_FILE = R"(BondEq.db)"; |
| | | const std::string DATABASE_FILE = R"(UserManager.db)"; |
| | | |
| | | const std::string INITIAL_ADMIN_USERNAME = "admin"; |
| | | const std::string INITIAL_ADMIN_PASSWORD = "admin"; |
| | |
| | | char szPath[MAX_PATH]; |
| | | GetModuleFileName(NULL, szPath, MAX_PATH); |
| | | std::string exePath(szPath); |
| | | std::string dbDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\DB"; |
| | | std::string dbDir = exePath.substr(0, exePath.find_last_of("\\/")) + "\\DB\\"; |
| | | |
| | | // æ£æ¥å¹¶å建configæä»¶å¤¹ |
| | | DWORD fileAttr = GetFileAttributes(dbDir.c_str()); |