1. 细分工程师用户权限(扩展制程工程师和设备工程师)
2. 配方修复选空白配方列表时,新建按钮不可点击的问题
3. 新添加双击配方列表事件
| | |
| | | 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_NOTIFY(NM_CLICK, IDC_LIST_PPID, &CPageRecipe::OnClickListPPID) |
| | | ON_NOTIFY(NM_DBLCLK, IDC_LIST_PPID, &CPageRecipe::OnDblclkListPPID) |
| | | ON_CBN_SELCHANGE(IDC_COMBO_EQUIPMENT, &CPageRecipe::OnCbnSelchangeComboEquipment) |
| | | END_MESSAGE_MAP() |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnLvnItemChangedListPPID(NMHDR* pNMHDR, LRESULT* pResult) |
| | | void CPageRecipe::OnClickListPPID(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR); |
| | | LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); |
| | | // TODO: 在此添加控件通知处理程序代码 |
| | | *pResult = 0; |
| | | |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT); |
| | | int nEqSel = pComboBox->GetCurSel(); |
| | | int selectedCount = ListView_GetSelectedCount(m_listPPID.GetSafeHwnd()); |
| | | if (pComboBox == nullptr) { |
| | | return; |
| | | } |
| | | |
| | | GetDlgItem(IDC_BUTTON_NEW)->EnableWindow(nEqSel == 0 && selectedCount > 0); |
| | | GetDlgItem(IDC_BUTTON_MODIFY)->EnableWindow(/*nEqSel == 0 &&*/ selectedCount > 0); |
| | | GetDlgItem(IDC_BUTTON_DELETE)->EnableWindow(nEqSel == 0 && selectedCount > 0); |
| | | GetDlgItem(IDC_BUTTON_DELETE_ALL)->EnableWindow(nEqSel == 0 && selectedCount > 0); |
| | | int nItem = pNMItemActivate->iItem; |
| | | int nEqSel = pComboBox->GetCurSel(); |
| | | |
| | | GetDlgItem(IDC_BUTTON_NEW)->EnableWindow(nEqSel == 0); |
| | | GetDlgItem(IDC_BUTTON_MODIFY)->EnableWindow(nItem > 0); |
| | | GetDlgItem(IDC_BUTTON_DELETE)->EnableWindow(nEqSel == 0 && nItem > 0); |
| | | GetDlgItem(IDC_BUTTON_DELETE_ALL)->EnableWindow(nEqSel == 0 && nItem > 0); |
| | | } |
| | | |
| | | void CPageRecipe::OnDblclkListPPID(NMHDR* pNMHDR, LRESULT* pResult) |
| | | { |
| | | LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); |
| | | // TODO: 在此添加控件通知处理程序代码 |
| | | *pResult = 0; |
| | | |
| | | int nItem = pNMItemActivate->iItem; |
| | | if (nItem < 0) { |
| | | return; |
| | | } |
| | | |
| | | CString strText = m_listPPID.GetItemText(nItem, 2); |
| | | CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_EQUIPMENT); |
| | | int nEqSel = pComboBox->GetCurSel(); |
| | | if (nEqSel == CB_ERR) { |
| | | return; |
| | | } |
| | | |
| | | SERVO::CEquipment* pEq = (SERVO::CEquipment*)pComboBox->GetItemDataPtr(nEqSel); |
| | | if (pEq == nullptr) { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | void CPageRecipe::OnCbnSelchangeComboEquipment() |
| | |
| | | afx_msg void OnBnClickedButtonDelete(); |
| | | afx_msg void OnBnClickedButtonDeleteAll(); |
| | | afx_msg void OnBnClickedButtonRefresh(); |
| | | afx_msg void OnLvnItemChangedListPPID(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnClickListPPID(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnDblclkListPPID(NMHDR* pNMHDR, LRESULT* pResult); |
| | | afx_msg void OnCbnSelchangeComboEquipment(); |
| | | DECLARE_MESSAGE_MAP() |
| | | |
| | |
| | | #include "stdafx.h" |
| | | #include "stdafx.h" |
| | | #include "RecipeManager.h" |
| | | #include <sstream> |
| | | #include <iomanip> |
| | |
| | | return false; |
| | | } |
| | | |
| | | // 启用 SQLite 的外键约束支持 |
| | | // 启用 SQLite 的外键约束支持 |
| | | if (!m_pDB->executeQuery("PRAGMA foreign_keys = ON;")) { |
| | | std::cerr << "Failed to enable foreign keys." << std::endl; |
| | | return false; |
| | |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | |
| | | // 开始事务 |
| | | // 开始事务 |
| | | m_pDB->executeQuery("BEGIN TRANSACTION;"); |
| | | |
| | | std::ostringstream oss; |
| | |
| | | } |
| | | } |
| | | |
| | | // 提交事务 |
| | | // 提交事务 |
| | | m_pDB->executeQuery("COMMIT;"); |
| | | return true; |
| | | } |
| | |
| | | return false; |
| | | } |
| | | |
| | | // 检查 ppid 是否存在 |
| | | // 检查 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, recipe_name) VALUES ('" |
| | | << ppid << "', " |
| | |
| | | |
| | | std::lock_guard<std::recursive_mutex> lock(m_mutex); |
| | | |
| | | // 检查是否已经存在相同的 newPPID |
| | | // 检查是否已经存在相同的 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; |
| | |
| | | |
| | | addRecipe(recipe); |
| | | |
| | | addDeviceRecipe("Bonder1", 101, "标准工艺"); |
| | | addDeviceRecipe("Bonder1", 102, "改良工艺"); |
| | | addDeviceRecipe("Bonder1", 103, "高速模式"); |
| | | addDeviceRecipe("Bonder1", 101, "标准工艺"); |
| | | addDeviceRecipe("Bonder1", 102, "改良工艺"); |
| | | addDeviceRecipe("Bonder1", 103, "高速模式"); |
| | | |
| | | addDeviceRecipe("Bonder2", 101, "标准工艺"); |
| | | addDeviceRecipe("Bonder2", 102, "改良工艺"); |
| | | addDeviceRecipe("Bonder2", 103, "高速模式"); |
| | | addDeviceRecipe("Bonder2", 101, "标准工艺"); |
| | | addDeviceRecipe("Bonder2", 102, "改良工艺"); |
| | | addDeviceRecipe("Bonder2", 103, "高速模式"); |
| | | } |
| | | |
| | | bool RecipeManager::readRecipeFile(const std::string& filename) { |
| | |
| | | #ifndef RECIPE_MANAGER_H |
| | | #ifndef RECIPE_MANAGER_H |
| | | #define RECIPE_MANAGER_H |
| | | |
| | | #include <string> |
| | |
| | | #include <unordered_map> |
| | | #include "Database.h" |
| | | |
| | | // 单个设备配方映射信息 |
| | | // 单个设备配方映射信息 |
| | | struct DeviceRecipe { |
| | | int nDeviceID; // 设备ID |
| | | int nRecipeID; // 子配方ID |
| | | std::string strDeviceName; // 设备名称 |
| | | std::string strRecipeName; // 子配方名称 |
| | | int nDeviceID; // 设备ID |
| | | int nRecipeID; // 子配方ID |
| | | std::string strDeviceName; // 设备名称 |
| | | std::string strRecipeName; // 子配方名称 |
| | | }; |
| | | |
| | | // 配方信息 |
| | | // 配方信息 |
| | | struct RecipeInfo { |
| | | std::string strPPID; // 配方ID |
| | | std::string strDescription; // 配方描述 |
| | | std::string strCreateTime; // 创建时间 |
| | | std::vector<DeviceRecipe> vecDeviceList; // 关联的设备信息列表 |
| | | std::string strPPID; // 配方ID |
| | | std::string strDescription; // 配方描述 |
| | | std::string strCreateTime; // 创建时间 |
| | | std::vector<DeviceRecipe> vecDeviceList; // 关联的设备信息列表 |
| | | }; |
| | | |
| | | using RecipeMap = std::unordered_map<std::string, RecipeInfo>; // 按 PPID 映射的配方表 |
| | | using RecipeMap = std::unordered_map<std::string, RecipeInfo>; // 按 PPID 映射的配方表 |
| | | |
| | | class RecipeManager { |
| | | public: |
| | | // 获取单例 |
| | | // 获取单例 |
| | | static RecipeManager& getInstance(); |
| | | |
| | | // 初始化配方数据库 |
| | | // 初始化配方数据库 |
| | | bool initRecipeTable(); |
| | | |
| | | // 销毁表或关闭连接 |
| | | // 销毁表或关闭连接 |
| | | void termRecipeTable(); |
| | | bool destroyRecipeTable(); |
| | | |
| | | // 检查 PPID 是否存在 |
| | | // 检查 PPID 是否存在 |
| | | bool ppidExists(const std::string& ppid); |
| | | |
| | | // 检查设备是否存在于指定 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 的设备配方 |
| | | // 删除指定 PPID 的设备配方 |
| | | bool deleteRecipeDeviceByID(const std::string& ppid, int nDeviceID); |
| | | |
| | | // 删除指定 PPID 的设备配方(通过设备名称) |
| | | // 删除指定 PPID 的设备配方(通过设备名称) |
| | | bool deleteRecipeDeviceByName(const std::string& ppid, const std::string& strDeviceName); |
| | | |
| | | // 查询所有配方 |
| | | // 查询所有配方 |
| | | std::vector<RecipeInfo> getAllRecipes(); |
| | | |
| | | // 根据 PPID 或描述查询配方 |
| | | // 根据 PPID 或描述查询配方 |
| | | std::vector<RecipeInfo> getRecipesByKeyword(const std::string& keyword); |
| | | |
| | | // 获取所有 PPID |
| | | // 获取所有 PPID |
| | | std::vector<std::string> getAllPPID() const; |
| | | |
| | | // 按 ID 查询 PPID |
| | | // 按 ID 查询 PPID |
| | | std::string getPPIDById(int nId); |
| | | |
| | | // 按 PPID 查询 ID |
| | | // 按 PPID 查询 ID |
| | | int getIdByPPID(const std::string& ppid); |
| | | |
| | | // 按 PPID 查询配方 |
| | | // 按 PPID 查询配方 |
| | | RecipeInfo getRecipeByPPID(const std::string& ppid); |
| | | |
| | | // 根据 PPID 和设备ID 获取设备配方ID |
| | | // 根据 PPID 和设备ID 获取设备配方ID |
| | | int getDeviceRecipeIDByID(const std::string& ppid, int nDeviceID); |
| | | |
| | | // 根据 PPID 和设备名称 获取设备配方ID |
| | | // 根据 PPID 和设备名称 获取设备配方ID |
| | | int getDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName); |
| | | |
| | | // 删除指定 PPID 的配方 |
| | | // 删除指定 PPID 的配方 |
| | | bool deleteRecipeByPPID(const std::string& ppid); |
| | | |
| | | // 更新指定 PPID 的配方 |
| | | // 更新指定 PPID 的配方 |
| | | bool updateRecipe(const RecipeInfo& recipe); |
| | | |
| | | // 更新 PPID(通过旧 PPID 和新 PPID) |
| | | // 更新 PPID(通过旧 PPID 和新 PPID) |
| | | bool updatePPID(const std::string& oldPPID, const std::string& newPPID); |
| | | |
| | | // 更新配方描述(通过 PPID) |
| | | // 更新配方描述(通过 PPID) |
| | | bool updateDescription(const std::string& ppid, const std::string& newDescription); |
| | | |
| | | // 更新设备配方ID(通过 PPID 和设备ID) |
| | | // 更新设备配方ID(通过 PPID 和设备ID) |
| | | bool updateDeviceRecipeIDByID(const std::string& ppid, int nDeviceID, int nNewRecipeID); |
| | | |
| | | // 更新设备配方ID(通过 PPID 和设备名称) |
| | | // 更新设备配方ID(通过 PPID 和设备名称) |
| | | bool updateDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName, int nNewRecipeID); |
| | | |
| | | bool addDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& strRecipeName); |
| | |
| | | bool deleteDeviceRecipe(const std::string& deviceName, int nRecipeID); |
| | | std::vector<std::pair<int, std::string>> getDeviceRecipes(const std::string& deviceName); |
| | | |
| | | // 模拟插入数据(测试用) |
| | | // 模拟插入数据(测试用) |
| | | void insertMockData(); |
| | | |
| | | // 读取配方文件(CSV 或 JSON) |
| | | // 读取配方文件(CSV 或 JSON) |
| | | bool readRecipeFile(const std::string& filename); |
| | | |
| | | // 保存配方到文件 |
| | | // 保存配方到文件 |
| | | bool saveRecipeFile(const std::string& filename); |
| | | |
| | | private: |
| | |
| | | |
| | | // 用户角色定义 |
| | | enum class UserRole { |
| | | SuperAdmin = 0, // 超级管理员 |
| | | Engineer, // 工程师 |
| | | Operator // 操作员 |
| | | SuperAdmin = 0, // 超级管理员:系统最高权限,管理所有用户和权限 |
| | | ProcessEngineer, // 制程工程师:负责工艺制定与优化 |
| | | EquipmentEngineer,// 设备工程师:负责设备维护与技术支持 |
| | | Operator, // 操作员:执行日常生产操作 |
| | | Unknown // 未知角色:默认或未识别的角色 |
| | | }; |
| | | |
| | | // 用户管理类,采用单例模式 |
| | |
| | | m_gridUserManager.ExpandLastColumn(); // 最后一列填充网格 |
| | | |
| | | m_mapRoleDescriptions.clear(); |
| | | m_mapRoleDescriptions.emplace(_T("管理员"), _T("管理所有用户,分配权限")); |
| | | m_mapRoleDescriptions.emplace(_T("工程师"), _T("维护系统,解决技术问题")); |
| | | m_mapRoleDescriptions.emplace(_T("操作员"), _T("执行日常操作任务")); |
| | | m_mapRoleDescriptions.emplace(_T("管理员"), _T("管理所有用户账户,分配和调整权限,负责系统安全与整体运行")); |
| | | m_mapRoleDescriptions.emplace(_T("制程工程师"), _T("负责生产工艺的制定、优化与改进,确保工艺稳定和良率提升")); |
| | | m_mapRoleDescriptions.emplace(_T("设备工程师"), _T("维护和保养设备,处理故障,保障设备稳定运行,参与技术升级")); |
| | | m_mapRoleDescriptions.emplace(_T("操作员"), _T("按照标准流程执行日常操作任务,监控生产状况,及时反馈异常")); |
| | | |
| | | FillUserManager(); |
| | | } |
| | |
| | | |
| | | CStringArray permissions; |
| | | permissions.Add(_T("管理员")); |
| | | permissions.Add(_T("工程师")); |
| | | permissions.Add(_T("制程工程师")); |
| | | permissions.Add(_T("设备工程师")); |
| | | permissions.Add(_T("操作员")); |
| | | |
| | | int nCols = m_gridUserManager.GetColumnCount(); |
| | |
| | | pCell->SetOptions(permissions); |
| | | pCell->SetStyle(CBS_DROPDOWNLIST); |
| | | |
| | | CString cstrRole = m_gridUserManager.GetItemText(i, 3); |
| | | int nRole = _ttoi(cstrRole); |
| | | if (nRole < 0 || nRole > 2) { |
| | | int nRole = _ttoi(m_gridUserManager.GetItemText(i, 3)); |
| | | if (nRole < 0 || nRole > 3) { |
| | | CString cstrMessage; |
| | | cstrMessage.Format(_T("用户 [%s],权限异常!将设置成操作员!"), m_gridUserManager.GetItemText(i, 1)); |
| | | AfxMessageBox(cstrMessage); |
| | | nRole = 2; |
| | | nRole = 3; |
| | | } |
| | | |
| | | m_gridUserManager.SetItemText(i, 3, permissions.GetAt(nRole)); |
| | |
| | | // 第四列设置(权限列)为下拉框 |
| | | CStringArray permissions; |
| | | permissions.Add(_T("管理员")); |
| | | permissions.Add(_T("工程师")); |
| | | permissions.Add(_T("制程工程师")); |
| | | permissions.Add(_T("设备工程师")); |
| | | permissions.Add(_T("操作员")); |
| | | |
| | | if (pGridCtrl->SetCellType(newRowIndex, 3, RUNTIME_CLASS(CGridCellCombo))) { |
| | |
| | | |
| | | CStringArray permissions; |
| | | permissions.Add(_T("管理员")); |
| | | permissions.Add(_T("工程师")); |
| | | permissions.Add(_T("制程工程师")); |
| | | permissions.Add(_T("设备工程师")); |
| | | permissions.Add(_T("操作员")); |
| | | |
| | | if (m_gridUserManager.SetCellType(row, 3, RUNTIME_CLASS(CGridCellCombo))) { |
| | |
| | | if (j == 3) { |
| | | if (cellText == _T("管理员")) |
| | | cellString = "0"; |
| | | else if (cellText == _T("工程师")) |
| | | else if (cellText == _T("制程工程师")) |
| | | cellString = "1"; |
| | | else if (cellText == _T("设备工程师")) |
| | | cellString = "2"; |
| | | else if (cellText == _T("操作员")) |
| | | cellString = "2"; |
| | | cellString = "3"; |
| | | else |
| | | cellString = "2"; |
| | | cellString = "3"; |
| | | } |
| | | |
| | | rowData.push_back(cellString); |