LAPTOP-T815PCOQ\25526
2024-12-17 876ad153fd25c0605f516b0f3a6407999db69131
1. 完善配方列表模块,与对称轴交互 2. 双击修改配方描述 3.配方列表控件动态变化
已修改9个文件
389 ■■■■■ 文件已修改
SourceCode/Bond/BondEq/BondEqDlg.cpp 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/CComponentPLCDlg.cpp 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/FileManager/RecipeManager.cpp 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/FileManager/RecipeManager.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/ToolUnits.cpp 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/ToolUnits.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/View/AxisSettingsDlg.cpp 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/View/RecipeListDlg.cpp 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/View/RecipeListDlg.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/BondEq/BondEqDlg.cpp
@@ -256,7 +256,9 @@
    std::string strRecipePath =  CToolUnits::getRecipePath();
    CToolUnits::createDir(strRecipePath.c_str());
    recipeManager.setRecipeFolder(strRecipePath);
    if (!recipeManager.loadRecipe("Default")) {
        AfxMessageBox("Default 配方加载失败!");
    }
    // 菜单
    CMenu menu;
SourceCode/Bond/BondEq/CComponentPLCDlg.cpp
@@ -152,9 +152,14 @@
void CComponentPLCDlg::OnBnClickedButtonAxisSetting()
{
    std::string strName = RecipeManager::getInstance().getCurrentRecipeName();
    if (strName.empty()) {
        AfxMessageBox("未选择配方!", MB_ICONERROR);
    }
    CAxisSettingsDlg axisDlg;
    axisDlg.SetPLC((CPLC*)m_pContext);
    axisDlg.SetRecipeName(_T("Default"));
    axisDlg.SetRecipeName(strName.c_str());
    axisDlg.DoModal();
}
SourceCode/Bond/BondEq/FileManager/RecipeManager.cpp
@@ -10,7 +10,7 @@
}
// 构造函数
RecipeManager::RecipeManager() : m_recipeFolder("Recipe") {}
RecipeManager::RecipeManager() : m_recipeFolder("Recipe"), m_currentRecipeName("") {}
// 加载轴信息
bool RecipeManager::loadAxes(pugi::xml_node axesNode) {
@@ -125,6 +125,11 @@
    m_recipeFolder = folderPath;
}
// 获取当前配方名称
std::string RecipeManager::getCurrentRecipeName() const {
    return m_currentRecipeName;
}
// 加载配方(如果文件不存在,加载默认数据)
bool RecipeManager::loadRecipe(const std::string& recipeName) {
    std::string filePath = m_recipeFolder + "/" + recipeName + ".xml";
@@ -132,9 +137,9 @@
    if (!doc.load_file(filePath.c_str())) {
        std::cerr << "Recipe file not found: " << filePath << ". Loading default recipe." << std::endl;
        generateDefaultRecipe();
        return false; // 文件不存在,但加载了默认数据
        return false; // 文件不存在
    }
    m_currentRecipeName = recipeName;
    auto recipeNode = doc.child("Recipe");
    auto axesNode = recipeNode.child("Axes");
@@ -170,13 +175,12 @@
// 生成默认配方
void RecipeManager::generateDefaultRecipe() {
    m_axes.clear();
    m_currentRecipeName = "Default";
    for (int axisId = 1; axisId <= 12; ++axisId) {
        AxisInfo axisInfo;
        axisInfo.id = axisId;
        axisInfo.positioningPointCount = 25;
        //axisInfo.maxPositioningSpeed = 100.0;
        //axisInfo.maxManualSpeed = 100.0;
        axisInfo.number = "M100-M" + std::to_string(axisId);
        axisInfo.description = "Default_Axis" + std::to_string(axisId);
        axisInfo.startAddress = "ZR" + std::to_string(10000 + (axisId - 1) * 300);
SourceCode/Bond/BondEq/FileManager/RecipeManager.h
@@ -52,6 +52,9 @@
    // 设置配方文件夹路径
    void setRecipeFolder(const std::string& folderPath);
    // 获取当前配方名称
    std::string getCurrentRecipeName() const;
    // 加载配方(文件不存在时加载默认数据)
    bool loadRecipe(const std::string& recipeName);
@@ -93,6 +96,7 @@
    void saveAxes(pugi::xml_node& axesNode);
private:
    std::string m_currentRecipeName; // 当前配方名称
    std::string m_recipeFolder;      // 配方文件夹路径
    std::map<int, AxisInfo> m_axes;  // 轴信息缓存
};
SourceCode/Bond/BondEq/ToolUnits.cpp
@@ -287,3 +287,17 @@
{
    return getCurrentExePath() + "\\Recipe";
}
std::string CToolUnits::getCurrentTimeString()
{
    struct tm ltm;
    time_t now = time(0);
    localtime_s(&ltm, &now);  // 使用安全的 localtime_s 函数
    char buffer[256];
    sprintf_s(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d",
        ltm.tm_year + 1900, ltm.tm_mon + 1, ltm.tm_mday,
        ltm.tm_hour, ltm.tm_min, ltm.tm_sec);
    return std::string(buffer);
}
SourceCode/Bond/BondEq/ToolUnits.h
@@ -27,5 +27,6 @@
    static void setDlgItemDouble(CWnd* pWnd, int nCtrlId, double value);
    static std::vector<CString> GetFileNamesInDirectory(const CString& strFolderPath, const CString& strExtension);
    static std::string getRecipePath();
    static std::string getCurrentTimeString();
};
SourceCode/Bond/BondEq/View/AxisSettingsDlg.cpp
@@ -205,6 +205,10 @@
void CAxisSettingsDlg::AdjustLabelFont(CBLLabel& label)
{
    if (label.m_hWnd == nullptr) {
        return;
    }
    // 获取控件的矩形区域
    CRect rect;
    label.GetClientRect(&rect);
SourceCode/Bond/BondEq/View/RecipeListDlg.cpp
@@ -19,11 +19,120 @@
CRecipeListDlg::CRecipeListDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_RECIPE_LIST, pParent)
{
    m_nInitialWidth = 0;
    m_nInitialHeight = 0;
    m_staticCurrRecipe = new CBLLabel();
}
CRecipeListDlg::~CRecipeListDlg()
{
    if (m_staticCurrRecipe != nullptr) {
        delete m_staticCurrRecipe;
        m_staticCurrRecipe = nullptr;
    }
    for (auto& pair : m_mapFonts) {
        if (pair.second) {
            pair.second->DeleteObject();
            delete pair.second;
        }
    }
    m_mapFonts.clear();
}
CFont* CRecipeListDlg::GetOrCreateFont(int nFontSize)
{
    auto it = m_mapFonts.find(nFontSize);
    if (it != m_mapFonts.end()) {
        return it->second;
    }
    CFont* font = new CFont();
    LOGFONT logFont = { 0 };
    _tcscpy_s(logFont.lfFaceName, _T("Segoe UI"));
    logFont.lfHeight = -nFontSize;
    logFont.lfQuality = CLEARTYPE_QUALITY;
    font->CreateFontIndirect(&logFont);
    m_mapFonts[nFontSize] = font;
    return font;
}
void CRecipeListDlg::AdjustControls(float dScaleX, float dScaleY)
{
    CWnd* pWnd = GetWindow(GW_CHILD);
    while (pWnd) {
        int nCtrlID = pWnd->GetDlgCtrlID();
        if (nCtrlID != -1 && m_mapCtrlLayouts.find(nCtrlID) != m_mapCtrlLayouts.end()) {
            CRect originalRect = m_mapCtrlLayouts[nCtrlID];
            CRect newRect(
                static_cast<int>(originalRect.left * dScaleX),
                static_cast<int>(originalRect.top * dScaleY),
                static_cast<int>(originalRect.right * dScaleX),
                static_cast<int>(originalRect.bottom * dScaleY));
            TCHAR szClassName[256];
            GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
            if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
                CGridCtrl* pGridCtrl = (CGridCtrl*)pWnd;
                pGridCtrl->SetDefCellHeight(newRect.Height() / 21);
                pGridCtrl->ExpandColumnsToFit(TRUE);
                pGridCtrl->ExpandLastColumn();
                pGridCtrl->Invalidate();
                pGridCtrl->UpdateWindow();
            }
            pWnd->MoveWindow(&newRect);
            AdjustControlFont(pWnd, newRect.Width(), newRect.Height());
        }
        pWnd = pWnd->GetNextWindow();
    }
}
void CRecipeListDlg::AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight)
{
    TCHAR szClassName[256];
    GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
    // 跳过特殊控件(如 MFCGridCtrl)
    if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
        return;
    }
    // 根据控件高度动态调整字体大小
    int fontSize = nHeight / 2;
    if (fontSize < 8) fontSize = 8;
    if (fontSize < 24) fontSize = 24;
    // 获取或创建字体
    CFont* pFont = GetOrCreateFont(fontSize);
    pWnd->SetFont(pFont);
    pWnd->Invalidate(); // 刷新控件显示
}
void CRecipeListDlg::AdjustLabelFont(CBLLabel& label)
{
    if (label.m_hWnd == nullptr) {
        return;
    }
    // 获取控件的矩形区域
    CRect rect;
    label.GetClientRect(&rect);
    // 动态计算字体大小,基于控件的高度
    int fontSize = rect.Height() / 2; // 控件高度的一半作为字体大小
    if (fontSize < 8) fontSize = 8;   // 最小字体大小
    if (fontSize > 30) fontSize = 30; // 最大字体大小
    // 设置字体大小
    label.SetFontSize(fontSize);
    // 刷新控件显示
    label.Invalidate();
    label.UpdateWindow();
}
void CRecipeListDlg::InitRecipeLise()
@@ -56,17 +165,17 @@
    m_grid.SetItemText(nRowIdx, nColIdx++, _T("No."));
    m_grid.SetColumnWidth(nColIdx, 10);
    m_grid.SetItemText(nRowIdx, nColIdx++, _T("名称"));
    m_grid.SetColumnWidth(nColIdx, 50);
    m_grid.SetColumnWidth(nColIdx, 120);
    m_grid.SetItemText(nRowIdx, nColIdx++, _T("描述"));
    m_grid.SetColumnWidth(nColIdx, 30);
    m_grid.SetItemText(nRowIdx, nColIdx++, _T("创建时间"));
    m_grid.SetFixedRowSelection(FALSE);
    m_grid.SetFixedColumnSelection(FALSE);
    m_grid.SetEditable(TRUE);
    m_grid.SetRowResize(FALSE);
    m_grid.SetColumnResize(TRUE);
    m_grid.ExpandColumnsToFit(TRUE);
    m_grid.SetFixedRowSelection(FALSE);     // 设置固定行不可选中
    m_grid.SetFixedColumnSelection(FALSE);  // 设置固定列不可选中
    m_grid.SetEditable(FALSE);                // 设置单元格可编辑
    m_grid.SetRowResize(FALSE);                // 设置行不可调整大小
    m_grid.SetColumnResize(TRUE);            // 设置列可调整大小
    m_grid.ExpandColumnsToFit(TRUE);        // 自动调整列宽
    m_grid.SetListMode(TRUE);                // 启用列表模式
    m_grid.EnableSelection(TRUE);            // 启用选择
    m_grid.SetSingleRowSelection(TRUE);        // 自动整行高亮(限制为单行选择)
@@ -78,7 +187,7 @@
void CRecipeListDlg::FillRecipeLise()
{
    // 清除数据行,保留表头
    for (int i = 1; i < m_grid.GetRowCount(); i++) {
    for (int i = m_grid.GetRowCount() - 1; i > 0; --i) {
        m_grid.DeleteRow(i);
    }
@@ -126,17 +235,77 @@
        m_grid.SetItemText(rowIdx, 2, description);                                // 配方描述
        m_grid.SetItemText(rowIdx, 3, createTime);                                // 创建时间
        m_grid.SetItemState(rowIdx, 0, GVIS_READONLY);
        m_grid.SetItemState(rowIdx, 1, GVIS_READONLY);
        m_grid.SetItemState(rowIdx, 3, GVIS_READONLY);
        ++rowIdx;
    }
    m_grid.ExpandColumnsToFit(FALSE);
    m_grid.ExpandLastColumn();
    m_grid.ExpandColumnsToFit(FALSE);   // 自动调整列宽
    m_grid.ExpandLastColumn();            // 最后一列填充网格
    // 刷新网格控件
    m_grid.Invalidate();
    m_grid.UpdateWindow();
}
void CRecipeListDlg::UpdateDataFile(const CString& strRecipeName, const CString& strNewDescription)
{
    CStdioFile file;
    CFileException fe;
    // 打开文件以读取和写入
    CString strFilePath;
    strFilePath.Format(_T("%s\\RecipeList.txt"), CToolUnits::getRecipePath().c_str());
    if (file.Open(strFilePath, CFile::modeReadWrite | CFile::shareDenyNone, &fe)) {
        CString strLine;
        CStringArray arrLines;
        BOOL bFound = FALSE;
        // 读取文件内容到内存
        while (file.ReadString(strLine)) {
            // 将这一行按逗号分割
            CStringArray arrColumns;
            int nPos = 0;
            while ((nPos = strLine.Find(_T(','))) != -1) {
                arrColumns.Add(strLine.Left(nPos));
                strLine = strLine.Mid(nPos + 1);
            }
            arrColumns.Add(strLine); // 最后一列
            // 如果第一列(配方名称)匹配,修改第二列(配方描述)
            if (arrColumns.GetSize() > 0 && arrColumns[0] == strRecipeName) {
                arrColumns[1] = strNewDescription; // 修改配方描述列
                bFound = TRUE;
            }
            // 将修改后的行保存回内存
            CString strUpdatedLine;
            for (int i = 0; i < arrColumns.GetSize(); ++i) {
                if (i > 0) strUpdatedLine += _T(",");
                strUpdatedLine += arrColumns[i];
            }
            arrLines.Add(strUpdatedLine);
        }
        if (bFound) {
            // 如果找到了配方名称,保存修改后的内容回文件
            file.SeekToBegin();
            file.SetLength(0);  // 清空文件内容
            for (int i = 0; i < arrLines.GetSize(); ++i) {
                file.WriteString(arrLines[i] + _T("\n"));
            }
        }
        else {
            // 如果没有找到配方名称,则追加新配方
            CString strNewLine;
            strNewLine.Format(_T("%s,%s,%s"), strRecipeName, strNewDescription, CToolUnits::getCurrentTimeString().c_str());
            file.SeekToEnd();
            file.WriteString(strNewLine + _T("\n"));
        }
        file.Close();
    }
    else {
        AfxMessageBox(_T("无法打开文件"));
    }
}
void CRecipeListDlg::DoDataExchange(CDataExchange* pDX)
@@ -149,6 +318,9 @@
BEGIN_MESSAGE_MAP(CRecipeListDlg, CDialogEx)
    ON_BN_CLICKED(IDC_BUTTON_CREATE_RECIPE, &CRecipeListDlg::OnBnClickedButtonCreateRecipe)
    ON_BN_CLICKED(IDC_BUTTON_DELETE_RECIPE, &CRecipeListDlg::OnBnClickedButtonDeleteRecipe)
    ON_BN_CLICKED(IDC_BUTTON_SELECT_RECIPE, &CRecipeListDlg::OnBnClickedButtonSelectRecipe)
    ON_NOTIFY(NM_DBLCLK, IDC_CUSTOM_RECIPE_LIST, &CRecipeListDlg::OnGridCellEditFinished)
    ON_WM_SIZE()
END_MESSAGE_MAP()
@@ -160,11 +332,87 @@
    CDialogEx::OnInitDialog();
    // TODO:  在此添加额外的初始化
    m_staticCurrRecipe->SubclassDlgItem(IDC_STATIC_CURR_RECIPE, this);
    m_staticCurrRecipe->SetBkColor(RGB(0, 110, 110));
    m_staticCurrRecipe->ModifyStyle(0, SS_NOTIFY);
    m_staticCurrRecipe->SetTextColor(RGB(255, 255, 255));
    m_staticCurrRecipe->SetAlignment(AlignCenter);
    m_staticCurrRecipe->SetDynamicFont(TRUE);
    m_staticCurrRecipe->SetWindowText(RecipeManager::getInstance().getCurrentRecipeName().c_str());
    CRect screenRect, dlgRect, clientRect;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &screenRect, 0);
    GetClientRect(&clientRect);
    m_nInitialWidth = clientRect.Width();
    m_nInitialHeight = clientRect.Height();
    // 初始化默认字体
    CFont* pDefaultFont = GetOrCreateFont(12);
    // 遍历所有子控件,记录初始位置并设置默认字体
    CWnd* pWnd = GetWindow(GW_CHILD);
    while (pWnd) {
        int nCtrlID = pWnd->GetDlgCtrlID();
        if (nCtrlID != -1) {
            // 记录控件初始布局
            CRect ctrlRect;
            pWnd->GetWindowRect(&ctrlRect);
            ScreenToClient(&ctrlRect);
            m_mapCtrlLayouts[nCtrlID] = ctrlRect;
            // 跳过特殊控件(如 MFCGridCtrl)
            TCHAR szClassName[256];
            GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
            if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
                pWnd = pWnd->GetNextWindow();
                continue;
            }
            // 设置默认字体
            pWnd->SetFont(pDefaultFont);
        }
        pWnd = pWnd->GetNextWindow();
    }
    GetWindowRect(&dlgRect);
    int dlgWidth = dlgRect.Width() * 2;
    int dlgHeight = dlgRect.Height() * 2;
    if (dlgWidth > screenRect.Width()) {
        dlgWidth = screenRect.Width();
    }
    if (dlgHeight > screenRect.Height()) {
        dlgHeight = screenRect.Height();
    }
    int centerX = screenRect.left + (screenRect.Width() - dlgWidth) / 2;
    int centerY = screenRect.top + (screenRect.Height() - dlgHeight) / 2;
    MoveWindow(centerX, centerY, dlgWidth, dlgHeight);
    InitRecipeLise();
    return TRUE;  // return TRUE unless you set the focus to a control
    // 异常: OCX 属性页应返回 FALSE
}
void CRecipeListDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    // TODO: 在此处添加消息处理程序代码
    if (nType == SIZE_MINIMIZED || m_mapCtrlLayouts.empty()) {
        return;
    }
    float dScaleX = static_cast<float>(cx) / m_nInitialWidth;
    float dScaleY = static_cast<float>(cy) / m_nInitialHeight;
    // 遍历对话框中的所有控件
    AdjustControls(dScaleX, dScaleY);
    AdjustLabelFont(*m_staticCurrRecipe);
}
void CRecipeListDlg::OnBnClickedButtonCreateRecipe()
{
@@ -298,3 +546,50 @@
    // 刷新网格控件
    FillRecipeLise();
}
void CRecipeListDlg::OnBnClickedButtonSelectRecipe()
{
    // TODO: 在此添加控件通知处理程序代码
    int nSelect = -1;
    for (int i = 1; i < m_grid.GetRowCount(); i++) {
        if (m_grid.IsCellSelected(i, 1)) {
            nSelect = i;
            break;
        }
    }
    if (nSelect < 0) {
        AfxMessageBox(_T("请选择配方!"));
        return;
    }
    CString strSelectedRecipe = m_grid.GetItemText(nSelect, 1);
    if (RecipeManager::getInstance().loadRecipe(std::string(CT2A(strSelectedRecipe)))) {
        m_staticCurrRecipe->SetWindowText(strSelectedRecipe);
    }
    else {
        AfxMessageBox(_T("加载配方失败!"));
    }
}
void CRecipeListDlg::OnGridCellEditFinished(NMHDR* pNotifyStruct, LRESULT* pResult)
{
    // 获取修改的行列信息
    NM_GRIDVIEW* pItem = (NM_GRIDVIEW*)pNotifyStruct;
    int nRow = pItem->iRow;
    int nCol = pItem->iColumn;
    // 只处理“配方描述”列的修改
    if (nCol == 2) {
        CString strRecipeName = m_grid.GetItemText(nRow, 1);
        CInputDialog inputDialog(_T("配方描述"), _T("请输入配方描述:"));
        if (inputDialog.DoModal() != IDOK) {
            *pResult = 0;
            return;
        }
        CString strText = inputDialog.GetInputText();
        UpdateDataFile(strRecipeName, strText);
    }
    *pResult = 0;
}
SourceCode/Bond/BondEq/View/RecipeListDlg.h
@@ -1,6 +1,7 @@
#pragma once
#include "afxdialogex.h"
#include "GridCtrl.h"
#include "BLLabel.h"
// CRecipeListDlg 对话框
@@ -19,16 +20,31 @@
#endif
private:
    CFont* GetOrCreateFont(int nFontSize);
    void AdjustControls(float dScaleX, float dScaleY);
    void AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight);
    void AdjustLabelFont(CBLLabel& label);
    void InitRecipeLise();
    void FillRecipeLise();
    void UpdateDataFile(const CString& strRecipeName, const CString& strNewDescription);
private:
    int m_nInitialWidth;
    int m_nInitialHeight;
    std::map<int, CFont*> m_mapFonts;        // 字体映射
    std::map<int, CRect> m_mapCtrlLayouts;    // 控件布局映射
    // 控件
    CBLLabel* m_staticCurrRecipe;
    CGridCtrl m_grid;
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    virtual BOOL OnInitDialog();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnBnClickedButtonCreateRecipe();
    afx_msg void OnBnClickedButtonDeleteRecipe();
    afx_msg void OnBnClickedButtonSelectRecipe();
    afx_msg void OnGridCellEditFinished(NMHDR* pNotifyStruct, LRESULT* pResult);
    DECLARE_MESSAGE_MAP()
};