1. 细分工程师用户权限(扩展制程工程师和设备工程师)
2. 配方修复选空白配方列表时,新建按钮不可点击的问题
3. 新添加双击配方列表事件
已修改7个文件
1468 ■■■■ 文件已修改
SourceCode/Bond/Servo/PageRecipe.cpp 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PageRecipe.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp 332 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeManager.cpp 892 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeManager.h 150 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/UserManager.h 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/UserManagerDlg.cpp 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PageRecipe.cpp
@@ -237,7 +237,8 @@
    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()
@@ -575,19 +576,48 @@
    }
}
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()
@@ -614,4 +644,4 @@
        SERVO::CRecipeList* pRecipeList = pEq->getRecipeList(0);
        FillRecipeListToListCtrl(pRecipeList);
    }
}
}
SourceCode/Bond/Servo/PageRecipe.h
@@ -37,7 +37,8 @@
    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()
SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp
@@ -14,12 +14,12 @@
// 绑定界面需要显示的设备
static const std::vector<DeviceMetaInfo> g_vecBindDevices = {
    { EQ_ID_VACUUMBAKE,      EQ_NAME_VACUUMBAKE },
    { EQ_ID_Bonder1,         EQ_NAME_BONDER1 },
    { EQ_ID_Bonder2,         EQ_NAME_BONDER2 },
    { EQ_ID_BAKE_COOLING,    EQ_NAME_BAKE_COOLING },
    { EQ_ID_MEASUREMENT,     EQ_NAME_MEASUREMENT },
    { EQ_ID_EFEM,            EQ_NAME_EFEM }
    { EQ_ID_VACUUMBAKE,      EQ_NAME_VACUUMBAKE },
    { EQ_ID_Bonder1,         EQ_NAME_BONDER1 },
    { EQ_ID_Bonder2,         EQ_NAME_BONDER2 },
    { EQ_ID_BAKE_COOLING,    EQ_NAME_BAKE_COOLING },
    { EQ_ID_MEASUREMENT,     EQ_NAME_MEASUREMENT },
    { EQ_ID_EFEM,            EQ_NAME_EFEM }
};
// CRecipeDeviceBindDlg 对话框
@@ -28,8 +28,8 @@
CRecipeDeviceBindDlg::CRecipeDeviceBindDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_RECIPE_DEVICE_BIND, pParent)
    , m_strPPID(_T(""))
    , m_strDesc(_T(""))
    , m_strPPID(_T(""))
    , m_strDesc(_T(""))
{
}
@@ -39,122 +39,122 @@
}
const RecipeInfo& CRecipeDeviceBindDlg::GetRecipeInfo() const {
    return m_recipe;
    return m_recipe;
}
void CRecipeDeviceBindDlg::SetRecipeInfo(const RecipeInfo& info)
{
    m_recipe = 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();
    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);
    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* 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);
        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 });
    }
        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& master = theApp.m_model.getMaster();
    auto* pEq = master.getEquipment(nDeviceID);
    if (!pEq) {
        return false;
    }
    auto* pRecipeList = pEq->getRecipeList(0);
    if (!pRecipeList) {
        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;
    auto& mapRecipeIds = pRecipeList->getIds();
    bool bFound = false;
    pCombo->ResetContent();
    for (const auto& pair : mapRecipeIds) {
        int nRecipeID = pair.second;
        std::string strRecipeName = RecipeManager::getInstance().getDeviceRecipeName(pEq->getName(), nRecipeID);
        if (strRecipeName.empty()) {
            strRecipeName = std::to_string(nRecipeID);
        }
        std::string strRecipeName = RecipeManager::getInstance().getDeviceRecipeName(pEq->getName(), nRecipeID);
        if (strRecipeName.empty()) {
            strRecipeName = std::to_string(nRecipeID);
        }
        CString str;
        str.Format(_T("%s"), strRecipeName.c_str());
        int idx = pCombo->AddString(str);
        pCombo->SetItemData(idx, nRecipeID);
        if (nSelectedRecipeID == nRecipeID) {
            pCombo->SetCurSel(idx);
            bFound = true;
        }
    }
        CString str;
        str.Format(_T("%s"), strRecipeName.c_str());
        int idx = pCombo->AddString(str);
        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);
    }
    if (nSelectedRecipeID != -1 && !bFound) {
        pCombo->SetCurSel(CB_ERR);
    }
    else if (pCombo->GetCount() > 0 && nSelectedRecipeID == -1) {
        pCombo->SetCurSel(0);
    }
    return true;
    return true;
}
bool CRecipeDeviceBindDlg::FillDeviceInfo(int idx, int nDeviceID, const CString& strDeviceName, int nSelectedRecipeID)
{
    if (idx < 0 || idx >= (int)m_vecDevices.size()) {
        return false;
    }
    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);
    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;
    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);
    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)
    ON_WM_CLOSE()
    ON_WM_SIZE()
    ON_BN_CLICKED(IDOK, &CRecipeDeviceBindDlg::OnBnClickedOk)
END_MESSAGE_MAP()
@@ -165,62 +165,62 @@
    CDialogEx::OnInitDialog();
    // 设置对话框标题
    SetWindowText(m_recipe.vecDeviceList.empty() ? _T("新建配方") : _T("编辑配方"));
    SetWindowText(m_recipe.vecDeviceList.empty() ? _T("新建配方") : _T("编辑配方"));
    // 创建动态控件字体
    if (!m_font.m_hObject) {
        CFont* pDlgFont = GetFont();
        LOGFONT lf;
        if (pDlgFont && pDlgFont->GetLogFont(&lf)) {
            lf.lfHeight = -16;
            m_font.CreateFontIndirect(&lf);
        }
    }
    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;
    // 计算坐标
    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();
    // 清空旧控件
    ReleaseDeviceControls();
    // 创建新控件
    CreateDeviceControls(nXStart, nYStart, nTotalControlWidth, nRowHeight);
    // 创建新控件
    CreateDeviceControls(nXStart, nYStart, nTotalControlWidth, nRowHeight);
    auto& master = theApp.m_model.getMaster();
    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);
    // 填充内容
    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);
        }
    }
        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();
    CenterWindow();
    return TRUE;  // return TRUE unless you set the focus to a control
    // 异常: OCX 属性页应返回 FALSE
@@ -228,53 +228,53 @@
void CRecipeDeviceBindDlg::OnClose()
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    CDialogEx::OnClose();
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    CDialogEx::OnClose();
    // 清理控件
    ReleaseDeviceControls();
    // 清理控件
    ReleaseDeviceControls();
}
void CRecipeDeviceBindDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    CDialogEx::OnSize(nType, cx, cy);
    // TODO: 在此处添加消息处理程序代码
    // TODO: 在此处添加消息处理程序代码
}
void CRecipeDeviceBindDlg::OnBnClickedOk()
{
    // TODO: 在此添加控件通知处理程序代码
    UpdateData(TRUE);
    // 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);
    // 收集所有设备映射
    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);
        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);
    }
        m_recipe.vecDeviceList.push_back(info);
    }
    // 检查 PPID 是否为空
    if (m_strPPID.IsEmpty()) {
        AfxMessageBox(_T("配方 PPID 不能为空"));
        return;
    }
    // 检查 PPID 是否为空
    if (m_strPPID.IsEmpty()) {
        AfxMessageBox(_T("配方 PPID 不能为空"));
        return;
    }
    // PPID和描述
    m_recipe.strPPID = CT2A(m_strPPID);
    m_recipe.strDescription = CT2A(m_strDesc);
    // PPID和描述
    m_recipe.strPPID = CT2A(m_strPPID);
    m_recipe.strDescription = CT2A(m_strDesc);
    CDialogEx::OnOK();
    CDialogEx::OnOK();
}
SourceCode/Bond/Servo/RecipeManager.cpp
@@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "RecipeManager.h"
#include <sstream>
#include <iomanip>
@@ -8,453 +8,453 @@
std::recursive_mutex RecipeManager::m_mutex;
RecipeManager& RecipeManager::getInstance() {
    static RecipeManager instance;
    return instance;
    static RecipeManager instance;
    return instance;
}
RecipeManager::RecipeManager() {
    m_pDB = new BL::SQLiteDatabase();
    m_pDB = new BL::SQLiteDatabase();
}
RecipeManager::~RecipeManager() {
    if (m_pDB) {
        delete m_pDB;
        m_pDB = nullptr;
    }
    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);
    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;
    }
    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;
    }
    // 启用 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 (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            ppid TEXT NOT NULL UNIQUE,
            description TEXT,
            create_time TEXT DEFAULT (datetime('now', 'localtime'))
        );
    )";
    const std::string createRecipeTable = R"(
        CREATE TABLE IF NOT EXISTS recipes (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            ppid TEXT NOT NULL UNIQUE,
            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,
            recipe_name TEXT NOT NULL,
            FOREIGN KEY(ppid) REFERENCES recipes(ppid) ON DELETE CASCADE ON UPDATE CASCADE,
            UNIQUE (ppid, device_id),
            UNIQUE (ppid, device_name)
        );
    )";
    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,
            recipe_name TEXT 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);
    return m_pDB->executeQuery(createRecipeTable) && m_pDB->executeQuery(createDeviceTable);
}
void RecipeManager::termRecipeTable() {
    if (!m_pDB) {
        return;
    }
    if (!m_pDB) {
        return;
    }
    m_pDB->disconnect();
    m_pDB->disconnect();
}
bool RecipeManager::destroyRecipeTable() {
    if (!m_pDB) {
    if (!m_pDB) {
        return false;
    }
    }
    return m_pDB->executeQuery("DROP TABLE IF EXISTS recipe_devices;") && m_pDB->executeQuery("DROP TABLE IF EXISTS recipes;");
    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");
    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");
    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;
    }
    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::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);
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    // 开始事务
    m_pDB->executeQuery("BEGIN TRANSACTION;");
    // 开始事务
    m_pDB->executeQuery("BEGIN TRANSACTION;");
    std::ostringstream oss;
    oss << "INSERT OR REPLACE INTO recipes (ppid, description, create_time) VALUES ('"
        << recipe.strPPID << "', '"
        << recipe.strDescription << "', '"
        << strTime << "');";
    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;
    }
    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, recipe_name) VALUES ('"
            << recipe.strPPID << "', "
            << device.nDeviceID << ", '"
            << device.strDeviceName << "', "
            << device.nRecipeID << ", '"
            << device.strRecipeName << "');";
    for (const auto& device : recipe.vecDeviceList) {
        std::ostringstream devSql;
        devSql << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id, recipe_name) VALUES ('"
            << recipe.strPPID << "', "
            << device.nDeviceID << ", '"
            << device.strDeviceName << "', "
            << device.nRecipeID << ", '"
            << device.strRecipeName << "');";
        if (!m_pDB->executeQuery(devSql.str())) {
            std::cerr << "[AddRecipe] Failed to insert device mapping: " << device.nDeviceID << std::endl;
            m_pDB->executeQuery("ROLLBACK;");
            return false;
        }
    }
        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;
    // 提交事务
    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;
    }
    if (!m_pDB || ppid.empty() || device.nDeviceID <= 0 || device.nRecipeID <= 0) {
        std::cerr << "[addRecipeDevice] Invalid input." << std::endl;
        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 << "', "
        << device.nDeviceID << ", '"
        << device.strDeviceName << "', "
        << device.nRecipeID << ", '"
        << device.strRecipeName << "');";
    // 插入设备记录
    std::ostringstream oss;
    oss << "INSERT OR REPLACE INTO recipe_devices (ppid, device_id, device_name, recipe_id, recipe_name) VALUES ('"
        << ppid << "', "
        << device.nDeviceID << ", '"
        << device.strDeviceName << "', "
        << device.nRecipeID << ", '"
        << device.strRecipeName << "');";
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery(oss.str());
    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;
    }
    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::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());
    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;
    }
    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::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::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery(oss.str());
}
std::vector<RecipeInfo> RecipeManager::getAllRecipes() {
    if (!m_pDB) {
        return {};
    }
    if (!m_pDB) {
        return {};
    }
    std::vector<RecipeInfo> recipes;
    auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes;");
    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];
    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, recipe_name FROM recipe_devices WHERE ppid = '" << info.strPPID << "'ORDER BY id ASC;";
        auto devs = m_pDB->fetchResults(devQuery.str());
        std::ostringstream devQuery;
        devQuery << "SELECT device_id, device_name, recipe_id, recipe_name FROM recipe_devices WHERE ppid = '" << info.strPPID << "'ORDER BY id ASC;";
        auto devs = m_pDB->fetchResults(devQuery.str());
        for (const auto& dev : devs) {
            DeviceRecipe dr;
            try {
                dr.nDeviceID = std::stoi(dev[0]);
                dr.strDeviceName = dev[1];
                dr.nRecipeID = std::stoi(dev[2]);
        for (const auto& dev : devs) {
            DeviceRecipe dr;
            try {
                dr.nDeviceID = std::stoi(dev[0]);
                dr.strDeviceName = dev[1];
                dr.nRecipeID = std::stoi(dev[2]);
                dr.strRecipeName = dev[3];
            }
            catch (...) {
                std::cerr << "Invalid data in recipe_devices for PPID: " << info.strPPID << std::endl;
                continue;
            }
            info.vecDeviceList.push_back(dr);
        }
        recipes.push_back(info);
    }
            }
            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;
    return recipes;
}
std::vector<RecipeInfo> RecipeManager::getRecipesByKeyword(const std::string& keyword) {
    std::vector<RecipeInfo> recipes;
    if (!m_pDB || keyword.empty()) {
        return recipes;
    }
    std::vector<RecipeInfo> recipes;
    if (!m_pDB || keyword.empty()) {
        return recipes;
    }
    std::ostringstream query;
    query << "SELECT ppid, description, create_time FROM recipes "
        << "WHERE ppid LIKE '%" << keyword << "%' OR description LIKE '%" << keyword << "%';";
    std::ostringstream query;
    query << "SELECT ppid, description, create_time FROM recipes "
        << "WHERE ppid LIKE '%" << keyword << "%' OR description LIKE '%" << keyword << "%';";
    auto rows = m_pDB->fetchResults(query.str());
    for (const auto& row : rows) {
        if (row.size() >= 3) {
            RecipeInfo info;
            info.strPPID = row[0];
            info.strDescription = row[1];
            info.strCreateTime = row[2];
            recipes.push_back(info);
        }
    }
    return recipes;
    auto rows = m_pDB->fetchResults(query.str());
    for (const auto& row : rows) {
        if (row.size() >= 3) {
            RecipeInfo info;
            info.strPPID = row[0];
            info.strDescription = row[1];
            info.strCreateTime = row[2];
            recipes.push_back(info);
        }
    }
    return recipes;
}
std::vector<std::string> RecipeManager::getAllPPID() const {
    std::vector<std::string> vecPPID;
    std::vector<std::string> vecPPID;
    if (!m_pDB) {
        return vecPPID;
    }
    if (!m_pDB) {
        return vecPPID;
    }
    const std::string query = "SELECT ppid FROM recipes ORDER BY ppid;";
    auto result = m_pDB->fetchResults(query);
    const std::string query = "SELECT ppid FROM recipes ORDER BY ppid;";
    auto result = m_pDB->fetchResults(query);
    for (const auto& row : result) {
        if (!row.empty()) {
            vecPPID.push_back(row[0]);
        }
    }
    for (const auto& row : result) {
        if (!row.empty()) {
            vecPPID.push_back(row[0]);
        }
    }
    return vecPPID;
    return vecPPID;
}
std::string RecipeManager::getPPIDById(int nId) {
    if (!m_pDB) {
        return {};
    }
    if (!m_pDB) {
        return {};
    }
    std::ostringstream query;
    query << "SELECT ppid FROM recipes WHERE id = " << nId << ";";
    std::ostringstream query;
    query << "SELECT ppid FROM recipes WHERE id = " << nId << ";";
    auto rows = m_pDB->fetchResults(query.str());
    if (rows.empty() || rows[0].empty()) {
        return {};
    }
    auto rows = m_pDB->fetchResults(query.str());
    if (rows.empty() || rows[0].empty()) {
        return {};
    }
    return rows[0][0];
    return rows[0][0];
}
int RecipeManager::getIdByPPID(const std::string& ppid) {
    if (!m_pDB) {
        return -1;
    }
    if (!m_pDB) {
        return -1;
    }
    std::ostringstream query;
    query << "SELECT id FROM recipes WHERE ppid = '" << ppid << "';";
    std::ostringstream query;
    query << "SELECT id FROM recipes WHERE ppid = '" << ppid << "';";
    auto rows = m_pDB->fetchResults(query.str());
    if (rows.empty() || rows[0].empty()) {
        return -1;
    }
    auto rows = m_pDB->fetchResults(query.str());
    if (rows.empty() || rows[0].empty()) {
        return -1;
    }
    try {
        return std::stoi(rows[0][0]);
    }
    catch (...) {
        std::cerr << "Invalid id value for PPID: " << ppid << std::endl;
        return -1;
    }
    try {
        return std::stoi(rows[0][0]);
    }
    catch (...) {
        std::cerr << "Invalid id value for PPID: " << ppid << std::endl;
        return -1;
    }
}
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;
    }
    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];
    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, recipe_name FROM recipe_devices WHERE ppid = '" + ppid + "';");
    for (const auto& dev : devs) {
        DeviceRecipe dr;
        try {
            dr.nDeviceID = std::stoi(dev[0]);
            dr.strDeviceName = dev[1];
            dr.nRecipeID = std::stoi(dev[2]);
            dr.strRecipeName = dev[3];
        }
        catch (...) {
            std::cerr << "Invalid data in recipe_devices for PPID: " << ppid << std::endl;
            continue;
        }
        info.vecDeviceList.push_back(dr);
    }
    return info;
    auto devs = m_pDB->fetchResults("SELECT device_id, device_name, recipe_id, recipe_name FROM recipe_devices WHERE ppid = '" + ppid + "';");
    for (const auto& dev : devs) {
        DeviceRecipe dr;
        try {
            dr.nDeviceID = std::stoi(dev[0]);
            dr.strDeviceName = dev[1];
            dr.nRecipeID = std::stoi(dev[2]);
            dr.strRecipeName = dev[3];
        }
        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;
    }
    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 << ";";
    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;
    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;
    }
    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 << "';";
    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;
    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;
    }
    if (!m_pDB) {
        return false;
    }
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    return m_pDB->executeQuery("DELETE FROM recipes WHERE ppid = '" + ppid + "';");
    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 (!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);
    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;
    }
    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);
    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;
    }
    // 检查是否已经存在相同的 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;");
    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;
    }
    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;
    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;
    }
    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::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());
    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) {
@@ -463,7 +463,7 @@
    }
    std::ostringstream query;
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    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());
@@ -474,191 +474,191 @@
        return false;
    }
    std::ostringstream query;
    std::lock_guard<std::recursive_mutex> lock(m_mutex);
    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());
}
bool RecipeManager::addDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& strRecipeName) {
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0 || strRecipeName.empty()) {
        return false;
    }
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0 || strRecipeName.empty()) {
        return false;
    }
    std::ostringstream sql;
    sql << "CREATE TABLE IF NOT EXISTS " << deviceName << "_Recipes ("
        << "recipe_id INTEGER PRIMARY KEY, "
        << "recipe_name TEXT NOT NULL"
        << ");";
    m_pDB->executeQuery(sql.str());
    std::ostringstream sql;
    sql << "CREATE TABLE IF NOT EXISTS " << deviceName << "_Recipes ("
        << "recipe_id INTEGER PRIMARY KEY, "
        << "recipe_name TEXT NOT NULL"
        << ");";
    m_pDB->executeQuery(sql.str());
    std::ostringstream ins;
    ins << "INSERT OR REPLACE INTO " << deviceName << "_Recipes (recipe_id, recipe_name) VALUES ("
        << nRecipeID << ", '" << strRecipeName << "');";
    std::ostringstream ins;
    ins << "INSERT OR REPLACE INTO " << deviceName << "_Recipes (recipe_id, recipe_name) VALUES ("
        << nRecipeID << ", '" << strRecipeName << "');";
    std::lock_guard<std::recursive_mutex> lk(m_mutex);
    return m_pDB->executeQuery(ins.str());
    std::lock_guard<std::recursive_mutex> lk(m_mutex);
    return m_pDB->executeQuery(ins.str());
}
bool RecipeManager::updateDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& newName) {
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0 || newName.empty()) {
        return false;
    }
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0 || newName.empty()) {
        return false;
    }
    std::ostringstream sql;
    sql << "UPDATE " << deviceName << "_Recipes SET recipe_name='" << newName
        << "' WHERE recipe_id=" << nRecipeID << ";";
    std::ostringstream sql;
    sql << "UPDATE " << deviceName << "_Recipes SET recipe_name='" << newName
        << "' WHERE recipe_id=" << nRecipeID << ";";
    std::lock_guard<std::recursive_mutex> lk(m_mutex);
    return m_pDB->executeQuery(sql.str());
    std::lock_guard<std::recursive_mutex> lk(m_mutex);
    return m_pDB->executeQuery(sql.str());
}
std::string RecipeManager::getDeviceRecipeName(const std::string& deviceName, int nRecipeID) {
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0) {
        return "";
    }
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0) {
        return "";
    }
    std::ostringstream sql;
    sql << "SELECT recipe_name FROM " << deviceName << "_Recipes "
        << "WHERE recipe_id=" << nRecipeID << " LIMIT 1;";
    std::ostringstream sql;
    sql << "SELECT recipe_name FROM " << deviceName << "_Recipes "
        << "WHERE recipe_id=" << nRecipeID << " LIMIT 1;";
    auto rows = m_pDB->fetchResults(sql.str());
    if (!rows.empty() && !rows[0].empty()) {
        return rows[0][0];
    }
    return "";
    auto rows = m_pDB->fetchResults(sql.str());
    if (!rows.empty() && !rows[0].empty()) {
        return rows[0][0];
    }
    return "";
}
bool RecipeManager::deleteDeviceRecipe(const std::string& deviceName, int nRecipeID) {
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0) {
        return false;
    }
    if (!m_pDB || deviceName.empty() || nRecipeID <= 0) {
        return false;
    }
    std::ostringstream sql;
    sql << "DELETE FROM " << deviceName << "_Recipes WHERE recipe_id=" << nRecipeID << ";";
    std::ostringstream sql;
    sql << "DELETE FROM " << deviceName << "_Recipes WHERE recipe_id=" << nRecipeID << ";";
    std::lock_guard<std::recursive_mutex> lk(m_mutex);
    return m_pDB->executeQuery(sql.str());
    std::lock_guard<std::recursive_mutex> lk(m_mutex);
    return m_pDB->executeQuery(sql.str());
}
std::vector<std::pair<int, std::string>> RecipeManager::getDeviceRecipes(const std::string& deviceName) {
    std::vector<std::pair<int, std::string>> out;
    if (!m_pDB || deviceName.empty()) {
        return out;
    }
    std::vector<std::pair<int, std::string>> out;
    if (!m_pDB || deviceName.empty()) {
        return out;
    }
    std::ostringstream sql;
    sql << "SELECT recipe_id, recipe_name FROM " << deviceName << "_Recipes ORDER BY recipe_id;";
    std::ostringstream sql;
    sql << "SELECT recipe_id, recipe_name FROM " << deviceName << "_Recipes ORDER BY recipe_id;";
    auto rows = m_pDB->fetchResults(sql.str());
    for (const auto& r : rows) {
        if (r.size() < 2) continue;
        try {
            int id = std::stoi(r[0]);
            out.emplace_back(id, r[1]);
        }
        catch (...) {}
    }
    return out;
    auto rows = m_pDB->fetchResults(sql.str());
    for (const auto& r : rows) {
        if (r.size() < 2) continue;
        try {
            int id = std::stoi(r[0]);
            out.emplace_back(id, r[1]);
        }
        catch (...) {}
    }
    return out;
}
void RecipeManager::insertMockData() {
    if (!m_pDB) {
        return;
    }
    if (!m_pDB) {
        return;
    }
    RecipeInfo recipe;
    recipe.strPPID = "P1001";
    recipe.strDescription = "Main Board Burn-in";
    RecipeInfo recipe;
    recipe.strPPID = "P1001";
    recipe.strDescription = "Main Board Burn-in";
    recipe.vecDeviceList = {
        {9, 101, "VacuumBake", "VacuumBake"},
        {10, 102, "Bonder1", "Bonder1"},
        {11, 103, "Bonder2", "Bonder2"}
    };
    recipe.vecDeviceList = {
        {9, 101, "VacuumBake", "VacuumBake"},
        {10, 102, "Bonder1", "Bonder1"},
        {11, 103, "Bonder2", "Bonder2"}
    };
    addRecipe(recipe);
    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) {
    if (!m_pDB) {
        return false;
    }
    if (!m_pDB) {
        return false;
    }
    std::ifstream file(filename);
    if (!file.is_open()) {
        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
    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;
    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, 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, dev.strRecipeName, ',');
        std::getline(ss, description, ',');
        std::getline(ss, createTime, ',');
        std::getline(ss, description, ',');
        std::getline(ss, createTime, ',');
        auto& recipe = recipeMap[ppid];
        recipe.strPPID = ppid;
        recipe.strDescription = description;
        recipe.strCreateTime = createTime;
        recipe.vecDeviceList.push_back(dev);
    }
        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;
        }
    }
    for (const auto& pair : recipeMap) {
        if (!updateRecipe(pair.second)) {
            std::cerr << "Failed to update recipe from file: " << pair.first << std::endl;
        }
    }
    return true;
    return true;
}
bool RecipeManager::saveRecipeFile(const std::string& filename) {
    if (!m_pDB) {
        return false;
    }
    if (!m_pDB) {
        return false;
    }
    std::ofstream file(filename);
    if (!file.is_open()) {
        return false;
    }
    std::ofstream file(filename);
    if (!file.is_open()) {
        return false;
    }
    file << "PPID,DeviceID,DeviceName,RecipeID,RecipeName,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 << ","
    file << "PPID,DeviceID,DeviceName,RecipeID,RecipeName,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 << ","
                << dev.strRecipeName << ","
                << recipe.strDescription << ","
                << recipe.strCreateTime << "\n";
        }
    }
                << recipe.strDescription << ","
                << recipe.strCreateTime << "\n";
        }
    }
    return true;
    return true;
}
SourceCode/Bond/Servo/RecipeManager.h
@@ -1,4 +1,4 @@
#ifndef RECIPE_MANAGER_H
#ifndef RECIPE_MANAGER_H
#define RECIPE_MANAGER_H
#include <string>
@@ -7,121 +7,121 @@
#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();
    // 获取单例
    static RecipeManager& getInstance();
    // 初始化配方数据库
    bool initRecipeTable();
    // 初始化配方数据库
    bool initRecipeTable();
    // 销毁表或关闭连接
    void termRecipeTable();
    bool destroyRecipeTable();
    // 销毁表或关闭连接
    void termRecipeTable();
    bool destroyRecipeTable();
    // 检查 PPID 是否存在
    bool ppidExists(const std::string& ppid);
    // 检查 PPID 是否存在
    bool ppidExists(const std::string& ppid);
    // 检查设备是否存在于指定 PPID 的配方中
    // 检查设备是否存在于指定 PPID 的配方中
    bool deviceExists(const std::string& ppid, int nDeviceID);
    // 添加一个配方及其设备映射
    bool addRecipe(const RecipeInfo& recipe);
    // 添加一个配方及其设备映射
    bool addRecipe(const RecipeInfo& recipe);
    // 添加设备到指定配方
    bool addRecipeDevice(const std::string& ppid, const DeviceRecipe& device);
    // 添加设备到指定配方
    bool addRecipeDevice(const std::string& ppid, const DeviceRecipe& device);
    // 删除指定 PPID 的设备配方
    bool deleteRecipeDeviceByID(const std::string& ppid, int nDeviceID);
    // 删除指定 PPID 的设备配方
    bool deleteRecipeDeviceByID(const std::string& ppid, int nDeviceID);
    // 删除指定 PPID 的设备配方(通过设备名称)
    bool deleteRecipeDeviceByName(const std::string& ppid, const std::string& strDeviceName);
    // 删除指定 PPID 的设备配方(通过设备名称)
    bool deleteRecipeDeviceByName(const std::string& ppid, const std::string& strDeviceName);
    // 查询所有配方
    std::vector<RecipeInfo> getAllRecipes();
    // 查询所有配方
    std::vector<RecipeInfo> getAllRecipes();
    // 根据 PPID 或描述查询配方
    std::vector<RecipeInfo> getRecipesByKeyword(const std::string& keyword);
    // 根据 PPID 或描述查询配方
    std::vector<RecipeInfo> getRecipesByKeyword(const std::string& keyword);
    // 获取所有 PPID
    std::vector<std::string> getAllPPID() const;
    // 获取所有 PPID
    std::vector<std::string> getAllPPID() const;
    // 按 ID 查询 PPID
    std::string getPPIDById(int nId);
    // 按 ID 查询 PPID
    std::string getPPIDById(int nId);
    // 按 PPID 查询 ID
    int getIdByPPID(const std::string& ppid);
    // 按 PPID 查询 ID
    int getIdByPPID(const std::string& ppid);
    // 按 PPID 查询配方
    RecipeInfo getRecipeByPPID(const std::string& ppid);
    // 按 PPID 查询配方
    RecipeInfo getRecipeByPPID(const std::string& ppid);
    // 根据 PPID 和设备ID 获取设备配方ID
    int getDeviceRecipeIDByID(const std::string& ppid, int nDeviceID);
    // 根据 PPID 和设备ID 获取设备配方ID
    int getDeviceRecipeIDByID(const std::string& ppid, int nDeviceID);
    // 根据 PPID 和设备名称 获取设备配方ID
    int getDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName);
    // 根据 PPID 和设备名称 获取设备配方ID
    int getDeviceRecipeIDByName(const std::string& ppid, const std::string& strDeviceName);
    // 删除指定 PPID 的配方
    bool deleteRecipeByPPID(const std::string& ppid);
    // 删除指定 PPID 的配方
    bool deleteRecipeByPPID(const std::string& ppid);
    // 更新指定 PPID 的配方
    bool updateRecipe(const RecipeInfo& recipe);
    // 更新指定 PPID 的配方
    bool updateRecipe(const RecipeInfo& recipe);
    // 更新 PPID(通过旧 PPID 和新 PPID)
    bool updatePPID(const std::string& oldPPID, const std::string& newPPID);
    // 更新 PPID(通过旧 PPID 和新 PPID)
    bool updatePPID(const std::string& oldPPID, const std::string& newPPID);
    // 更新配方描述(通过 PPID)
    bool updateDescription(const std::string& ppid, const std::string& newDescription);
    // 更新配方描述(通过 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 和设备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);
    // 更新设备配方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 updateDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& newName);
    std::string getDeviceRecipeName(const std::string& deviceName, int nRecipeID);
    bool deleteDeviceRecipe(const std::string& deviceName, int nRecipeID);
    std::vector<std::pair<int, std::string>> getDeviceRecipes(const std::string& deviceName);
    bool addDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& strRecipeName);
    bool updateDeviceRecipe(const std::string& deviceName, int nRecipeID, const std::string& newName);
    std::string getDeviceRecipeName(const std::string& deviceName, int nRecipeID);
    bool deleteDeviceRecipe(const std::string& deviceName, int nRecipeID);
    std::vector<std::pair<int, std::string>> getDeviceRecipes(const std::string& deviceName);
    // 模拟插入数据(测试用)
    void insertMockData();
    // 模拟插入数据(测试用)
    void insertMockData();
    // 读取配方文件(CSV 或 JSON)
    bool readRecipeFile(const std::string& filename);
    // 读取配方文件(CSV 或 JSON)
    bool readRecipeFile(const std::string& filename);
    // 保存配方到文件
    bool saveRecipeFile(const std::string& filename);
    // 保存配方到文件
    bool saveRecipeFile(const std::string& filename);
private:
    RecipeManager();
    ~RecipeManager();
    RecipeManager();
    ~RecipeManager();
    RecipeManager(const RecipeManager&) = delete;
    RecipeManager& operator=(const RecipeManager&) = delete;
    RecipeManager(const RecipeManager&) = delete;
    RecipeManager& operator=(const RecipeManager&) = delete;
private:
    BL::Database* m_pDB;
    static std::recursive_mutex m_mutex;
    BL::Database* m_pDB;
    static std::recursive_mutex m_mutex;
};
#endif // RECIPE_MANAGER_H
SourceCode/Bond/Servo/UserManager.h
@@ -9,9 +9,11 @@
// 用户角色定义
enum class UserRole {
    SuperAdmin = 0,     // 超级管理员
    Engineer,           // 工程师
    Operator            // 操作员
    SuperAdmin = 0,   // 超级管理员:系统最高权限,管理所有用户和权限
    ProcessEngineer,  // 制程工程师:负责工艺制定与优化
    EquipmentEngineer,// 设备工程师:负责设备维护与技术支持
    Operator,         // 操作员:执行日常生产操作
    Unknown           // 未知角色:默认或未识别的角色
};
// 用户管理类,采用单例模式
SourceCode/Bond/Servo/UserManagerDlg.cpp
@@ -88,9 +88,10 @@
    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();
}
@@ -134,12 +135,13 @@
    CStringArray permissions;
    permissions.Add(_T("管理员"));
    permissions.Add(_T("工程师"));
    permissions.Add(_T("制程工程师"));
    permissions.Add(_T("设备工程师"));
    permissions.Add(_T("操作员"));
    int nCols = m_gridUserManager.GetColumnCount();
    for (int i = 1; i < m_gridUserManager.GetRowCount(); ++i) {
        m_gridUserManager.SetItemState(i, 0, GVIS_READONLY); // 第一列只读
        m_gridUserManager.SetItemState(i, 0, GVIS_READONLY);         // 第一列只读
        m_gridUserManager.SetItemState(i, nCols - 2, GVIS_READONLY); // 倒数第二列只读
        m_gridUserManager.SetItemState(i, nCols - 1, GVIS_READONLY); // 最后一列只读
@@ -149,13 +151,12 @@
            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));
@@ -237,7 +238,8 @@
    // 第四列设置(权限列)为下拉框
    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))) {
@@ -443,7 +445,8 @@
        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))) {
@@ -527,12 +530,14 @@
            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);