型材切割优化软件与 Excel 插件全面指南

型材切割优化软件是一款专为优化线性材料(如管道、棒材或型材)切割设计的工具,旨在最小化材料浪费并最大化利用率。该软件基于 C++ 和 Microsoft Foundation Class (MFC) 框架开发,采用模块化设计,提供用户友好的界面和强大的优化算法。本文按照用户界面、数据管理、优化引擎、方案管理和辅助工具的模块划分,详细介绍软件框架、使用方法和核心原理,并嵌入关键代码片段说明实现细节。此外,还将介绍两款基于贪心法实现的 Excel 插件(基于 VBA 的“下料优化EXCEL专业版”和基于 VSTO 的“ProfileCuttingExcel”),并进行性能对比,最后提供资源分享链接。

一、软件框架结构

软件采用模块化设计,主要分为以下几个核心模块:

控制指令
参数配置
提供输入
实时方案
可视化
支持
支持
支持
支持
用户界面
优化引擎
数据管理
方案管理
辅助工具

1. 用户界面模块

  • 功能:提供交互式图形界面,接收用户输入(零件/原料数据、参数配置),展示优化结果和日志信息,发送控制指令(如启动、暂停优化)。
  • 核心组件:
    • CGridCtrl (m_ItemGrid, m_BinGrid):用于显示和编辑零件(Items)和原料(Bins)数据,支持插入、删除、导入和导出操作。
    • CListCtrl (m_SolutionList):展示优化结果,包括原料用量、利用率等。
    • CEdit (m_TraceWnd):显示实时计算日志。
  • 实现细节:
    • 主对话框基于 MFC 的 CDialogEx 实现,负责控件初始化、事件处理和数据绑定。
    • 支持右键菜单操作(如插入行、删除行、导入/导出数据)。
    • 参数配置(如锯缝宽度 m_Kerf、快速模式 m_QuickMode)通过界面输入,传递给数据管理和优化引擎模块。
  • 关键代码:
    BOOL OnInitDialog()
    {
        CDialogEx::OnInitDialog();
        SetIcon(m_hIcon, TRUE); // 设置大图标
        SetIcon(m_hIcon, FALSE); // 设置小图标
        // 设置 SolutionList 样式
        DWORD dwStyle = m_SolutionList.GetExtendedStyle();
        dwStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES;
        m_SolutionList.SetExtendedStyle(dwStyle);
        m_SolutionList.InsertColumn(0, _T("序号"), LVCFMT_CENTER);
        m_SolutionList.InsertColumn(1, _T("迭次"), LVCFMT_CENTER);
        m_SolutionList.InsertColumn(2, _T("原料用量"), LVCFMT_CENTER);
        m_SolutionList.InsertColumn(3, _T("原料总长"), LVCFMT_CENTER);
        m_SolutionList.InsertColumn(4, _T("模数"), LVCFMT_CENTER);
        m_SolutionList.InsertColumn(5, _T("利用率(%)"), LVCFMT_CENTER);
        m_SolutionList.InsertColumn(6, _T("实际利用率(%)"), LVCFMT_CENTER);
        m_SolutionList.InsertColumn(7, _T("时间(ms)"), LVCFMT_CENTER);
        // 初始化 BinGrid
        m_BinGrid.SetHeaderSort(TRUE);
        m_BinGrid.EnableDragAndDrop(TRUE);
        m_BinGrid.SetEditable(TRUE);
        m_BinGrid.SetColumnCount(3);
        m_BinGrid.SetRowCount(1);
        m_BinGrid.SetFixedRowCount(1);
        m_BinGrid.SetFixedColumnCount(1);
        m_BinGrid.SetItemText(0, 0, _T("序号"));
        m_BinGrid.SetItemText(0, 1, _T("长度"));
        m_BinGrid.SetItemText(0, 2, _T("数量"));
        m_BinGrid.SetCompareFunction(CGridCtrl::pfnCellNumericCompare);
        m_BinGrid.ExpandColumnsToFit();
        // 初始化 ItemGrid
        m_ItemGrid.SetHeaderSort(TRUE);
        m_ItemGrid.EnableDragAndDrop(TRUE);
        m_ItemGrid.SetEditable(TRUE);
        m_ItemGrid.SetColumnCount(4);
        m_ItemGrid.SetRowCount(1);
        m_ItemGrid.SetFixedRowCount(1);
        m_ItemGrid.SetFixedColumnCount(1);
        m_ItemGrid.SetItemText(0, 0, _T("序号"));
        m_ItemGrid.SetItemText(0, 1, _T("长度"));
        m_ItemGrid.SetItemText(0, 2, _T("数量"));
        m_ItemGrid.SetItemText(0, 3, _T("备注"));
        m_ItemGrid.SetCompareFunction(CGridCtrl::pfnCellNumericCompare);
        initRandom();
        OnBnClickedButtonGenerateitem();
        m_ItemGrid.ExpandColumnsToFit();
        OnEnChangeEditValue();
        return TRUE;
    }
    

2. 数据管理模块

  • 功能:管理零件(ItemVec)和原料(BinVec)数据,包括输入、验证、存储、导入导出和过滤,为优化引擎提供输入。
  • 核心功能:
    • GetItemGridData / SetItemGridData:从/向 m_ItemGrid 读取/写入零件数据。
    • GetBinGridData / SetBinGridData:从/向 m_BinGrid 读取/写入原料数据。
    • OnBnClickedButtonGenerateitem:生成随机零件数据,基于原料长度范围。
    • OnBnClickedButtonImportdata / OnBnClickedButtonExportdata:支持 CSV 格式的数据导入和导出。
  • 实现细节:
    • 使用 rapidcsv 库处理 CSV 文件的读写。
    • 数据存储在 ItemVec 和 BinVec(std::vector 类型)中。
    • 数据过滤功能移除无效数据(长度或数量小于等于 0)。
  • 关键代码:
    void OnBnClickedButtonGenerateitem()
    {
        UpdateData(TRUE);
        GetBinGridData(m_BinGrid, m_Bins);
        if (m_Bins.empty())
        {
            AppendBinData({ 6000, 10000 });
            m_MaxBinLength = m_MinBinLength = 6000;
        }
        else
        {
            auto sortedLengths = from(m_Bins)
                .select([](const auto& bin) { return bin.length; })
                .orderBy()
                .toStdVector();
            m_MinBinLength = sortedLengths[0];
            m_MaxBinLength = sortedLengths.back();
        }
        m_ItemGrid.DeleteNonFixedRows();
        Length length = (m_MinBinLength + m_MaxBinLength) / 2;
        Length top = 3 * length / 5;
        Length bottom = length / 5 + 1;
        if (top == bottom || bottom < 1)
        {
            AfxMessageBox(L"原料长度设置不合理,请重新设置!");
            return;
        }
        m_ItemCount = 0;
        m_TotalItemLength = 0;
        int quantity = 0;
        initRandom();
        std::uniform_int_distribution<Length> distLength(bottom, top - 1);
        std::uniform_int_distribution<int> distQuantity(5, 100);
        for (int i = 0; i < 100; i++)
        {
            AppendRow(m_ItemGrid);
            length = distLength(rng);
            m_ItemGrid.SetItemText(i + 1, 1, CCommonUtils::ToString(length));
            quantity = distQuantity(rng);
            m_ItemGrid.SetItemText(i + 1, 2, CCommonUtils::ToString(quantity));
            m_ItemCount += quantity;
            m_TotalItemLength += length * quantity;
        }
        AppendRow(m_ItemGrid);
        UpdateFixedColumnNumbers(m_ItemGrid);
        UpdateData(FALSE);
    }
    
  • 关键代码:
    void OnBnClickedButtonImportdata()
    {
        std::vector<CString> vecFileType = { L"csv" };
        CString filePath;
        if (CCommonUtils::SelectFile(vecFileType, filePath))
        {
            if (CCommonUtils::IsFileExist(filePath))
            {
                using namespace rapidcsv;
                Document doc(CCommonUtils::UTF2ASCII(CCommonUtils::WCharToChar(filePath)));
                if (doc.GetColumnCount() > 3 || doc.GetColumnCount() < 2)
                {
                    AfxMessageBox(L"型材库文件数据文件格式错误,请保持和界面一致!");
                    return;
                }
                ItemVec items;
                items.reserve(doc.GetRowCount());
                m_ItemCount = 0;
                m_TotalItemLength = 0;
                for (size_t i = 0, row = doc.GetRowCount(); i < row; ++i)
                {
                    Item item;
                    item.length = doc.GetCell<Length>(0, i);
                    item.quantity = doc.GetCell<Quantity>(1, i);
                    if (item.length > 0 && item.quantity > 0)
                    {
                        items.push_back(item);
                        m_ItemCount += item.quantity;
                        m_TotalItemLength += item.length * item.quantity;
                    }
                }
                if (items.empty())
                    AfxMessageBox(L"无可用数据!");
                else
                {
                    SetItemGridData(m_ItemGrid, items);
                    CString message;
                    message.Format(L"导入成功,导入数据 %lld 条。 ", items.size());
                    AfxMessageBox(message);
                }
            }
            else
                AfxMessageBox(L"选择文件失败!");
        }
        UpdateData(FALSE);
    }
    

3. 优化引擎模块

  • 功能:执行型材切割优化算法,生成最优切割方案。
  • 核心组件:
    • pc_handle_t (pCut_):优化算法的核心句柄,调用外部库 ProfileCuttingPL 执行计算。
    • Solution (pc_solution_t):存储优化结果,包括原料数量、利用率等。
    • Parameters (pc_parameters_t):配置优化参数,如锯缝宽度 (m_Kerf)、快速模式 (m_QuickMode)。
  • 实现细节:
    • 支持多线程计算,异步执行优化任务,避免界面卡顿。
    • 依赖外部库(如 columngenerationsolver、Knapsack、treesearchsolver)实现列生成和动态规划算法。
    • 支持快速模式(忽略锯缝)和时间限制。
  • 关键代码:
    void UpdateSolution(const Solution& sol)
    {
        if (!solutions_.empty())
        {
            if (m_FilterSolution)
            {
                const auto& last = solutions_.back();
                if (last.binCount == sol.binCount
                    && sol.actualUtilization - last.actualUtilization < 1e-8
                    && last.solutionBinCount == sol.solutionBinCount)
                    return;
            }
        }
        if (solutions_.size() >= MAX_SOLUTIONS)
        {
            pc_free_solution(&solutions_.front());
            solutions_.pop_front();
            m_SolutionList.DeleteItem(0);
        }
        pc_solution_t sol_copy = {};
        pc_copy_solution(&sol_copy, &sol);
        solutions_.push_back(sol_copy);
        int nItem = m_SolutionList.GetItemCount();
        m_SolutionList.InsertItem(nItem, CCommonUtils::ToString(nItem + 1));
        m_SolutionList.SetItemText(nItem, 1, CCommonUtils::ToString(m_IterationCount));
        m_SolutionList.SetItemText(nItem, 2, CCommonUtils::ToString(sol.binCount));
        m_SolutionList.SetItemText(nItem, 3, CCommonUtils::ToString(1.0 * m_TotalBinLength, 0));
        m_SolutionList.SetItemText(nItem, 4, CCommonUtils::ToString(sol.solutionBinCount));
        m_SolutionList.SetItemText(nItem, 5, CCommonUtils::ToString(100.0 * m_UsedBinLength / m_TotalBinLength, 3));
        m_SolutionList.SetItemText(nItem, 6, CCommonUtils::ToString(100.0 * m_TotalItemLength / m_TotalBinLength, 3));
        m_SolutionList.SetItemText(nItem, 7, CCommonUtils::ToString(pc_get_elapsed_time(pCut_) * 1000.0, 3));
        for (int i = 0; i < nItem; ++i)
            m_SolutionList.SetItemText(i, 0, CCommonUtils::ToString(i + 1));
        if (!m_runCallback) return;
        CString message;
        message.Format(_T("第 %d 代:原料数量: %d,原料总长:%lld,模式数量:%d,利用率: %.3f %%,实际利用率: %.3f %%"),
            m_IterationCount++,
            sol.binCount,
            m_TotalBinLength,
            sol.solutionBinCount,
            100.0 * m_UsedBinLength / m_TotalBinLength,
            100.0 * m_TotalItemLength / m_TotalBinLength);
        SendTrace(message);
    }
    

4. 方案管理模块

  • 功能:存储、过滤和可视化优化引擎生成的切割方案,供用户查看和导出。
  • 核心功能:
    • UpdateSolution:更新和存储优化方案,显示在 m_SolutionList。
    • GetSolutionDetails:生成详细的切割方案描述。
    • OnSolutionExportToCSV / OnSolutionExportToTXT:导出优化方案到 CSV 或 TXT 文件。
  • 实现细节:
    • 使用 std::deque 存储最多 50 个优化方案(MAX_SOLUTIONS)。
    • 支持方案过滤(m_FilterSolution),去除重复或相似方案。
    • 优化结果通过 m_SolutionList 展示,支持点击查看详细信息。
  • 关键代码:
    void UpdateSolution(const Solution& sol)
    {
        if (!solutions_.empty())
        {
            if (m_FilterSolution)
            {
                const auto& last = solutions_.back();
                if (last.binCount == sol.binCount
                    && sol.actualUtilization - last.actualUtilization < 1e-8
                    && last.solutionBinCount == sol.solutionBinCount)
                    return;
            }
        }
        if (solutions_.size() >= MAX_SOLUTIONS)
        {
            pc_free_solution(&solutions_.front());
            solutions_.pop_front();
            m_SolutionList.DeleteItem(0);
        }
        pc_solution_t sol_copy = {};
        pc_copy_solution(&sol_copy, &sol);
        solutions_.push_back(sol_copy);
        int nItem = m_SolutionList.GetItemCount();
        m_SolutionList.InsertItem(nItem, CCommonUtils::ToString(nItem + 1));
        m_SolutionList.SetItemText(nItem, 1, CCommonUtils::ToString(m_IterationCount));
        m_SolutionList.SetItemText(nItem, 2, CCommonUtils::ToString(sol.binCount));
        m_SolutionList.SetItemText(nItem, 3, CCommonUtils::ToString(1.0 * m_TotalBinLength, 0));
        m_SolutionList.SetItemText(nItem, 4, CCommonUtils::ToString(sol.solutionBinCount));
        m_SolutionList.SetItemText(nItem, 5, CCommonUtils::ToString(100.0 * m_UsedBinLength / m_TotalBinLength, 3));
        m_SolutionList.SetItemText(nItem, 6, CCommonUtils::ToString(100.0 * m_TotalItemLength / m_TotalBinLength, 3));
        m_SolutionList.SetItemText(nItem, 7, CCommonUtils::ToString(pc_get_elapsed_time(pCut_) * 1000.0, 3));
        for (int i = 0; i < nItem; ++i)
            m_SolutionList.SetItemText(i, 0, CCommonUtils::ToString(i + 1));
        if (!m_runCallback) return;
        CString message;
        message.Format(_T("第 %d 代:原料数量: %d,原料总长:%lld,模式数量:%d,利用率: %.3f %%,实际利用率: %.3f %%"),
            m_IterationCount++,
            sol.binCount,
            m_TotalBinLength,
            sol.solutionBinCount,
            100.0 * m_UsedBinLength / m_TotalBinLength,
            100.0 * m_TotalItemLength / m_TotalBinLength);
        SendTrace(message);
    }
    

5. 辅助工具模块

  • 功能:提供随机数据生成、日志输出和配置管理等支持功能。

  • 核心功能:

    • initRandom / OnBnClickedButtonGenerateitem:生成随机零件数据,基于梅森旋转算法(std::mt19937)。
    • SendTrace / OnUpdateTrace:实时更新计算日志。
    • SaveConfiguration / LoadConfiguration:保存和加载用户配置。
  • 实现细节:

    • 使用 MFC 注册表操作存储配置(如 m_runCallback、m_FilterSolution、m_Kerf)。
    • 自动调整表格列宽(ExpandColumnsToFit)。
  • 关键代码:

    void SaveConfiguration()
    {
        AfxGetApp()->WriteProfileStringW(_T("ProfileCutting"), _T("RunCallback"), CCommonUtils::ToString(m_runCallback));
        AfxGetApp()->WriteProfileStringW(_T("ProfileCutting"), _T("FilterSolution"), CCommonUtils::ToString(m_FilterSolution));
        AfxGetApp()->WriteProfileStringW(_T("ProfileCutting"), _T("QuickMode"), CCommonUtils::ToString(m_QuickMode));
        AfxGetApp()->WriteProfileStringW(_T("ProfileCutting"), _T("SetTimer"), CCommonUtils::ToString(m_SetTimer));
        AfxGetApp()->WriteProfileStringW(_T("ProfileCutting"), _T("LimitTime"), CCommonUtils::ToString(m_LimitTime));
        AfxGetApp()->WriteProfileStringW(_T("ProfileCutting"), _T("Kerf"), CCommonUtils::ToString(m_Kerf));
    }
    void LoadConfiguration()
    {
        CString strValue;
        strValue = AfxGetApp()->GetProfileStringW(_T("ProfileCutting"), _T("RunCallback"), _T("1"));
        m_runCallback = (_ttoi(strValue) != 0);
        strValue = AfxGetApp()->GetProfileStringW(_T("ProfileCutting"), _T("FilterSolution"), _T("1"));
        m_FilterSolution = (_ttoi(strValue) != 0);
        strValue = AfxGetApp()->GetProfileStringW(_T("ProfileCutting"), _T("QuickMode"), _T("0"));
        m_QuickMode = (_ttoi(strValue) != 0);
        strValue = AfxGetApp()->GetProfileStringW(_T("ProfileCutting"), _T("SetTimer"), _T("0"));
        m_SetTimer = (_ttoi(strValue) != 0);
        strValue = AfxGetApp()->GetProfileStringW(_T("ProfileCutting"), _T("LimitTime"), _T("2.0"));
        m_LimitTime = _tcstod(strValue, nullptr);
        strValue = AfxGetApp()->GetProfileStringW(_T("ProfileCutting"), _T("Kerf"), _T("0"));
        m_Kerf = static_cast<Length>(_ttoi64(strValue));
        if (isTrial)
        {
            m_QuickMode = FALSE;
            GetDlgItem(IDC_CHECK_QuickMode)->EnableWindow(FALSE);
        }
        UpdateData(FALSE);
    }
    

二、Excel插件介绍

除了上述独立型材切割优化软件,我们还开发了两款基于 Excel 的插件,用于类似场景的切割优化。这两款插件基于贪心法实现,适合快速处理中小规模的切割任务。以下是它们的详细介绍:

1. 下料优化EXCEL专业版(基于 VBA)

  • 功能:

    • 提供 Excel 原生的界面,通过 VBA 宏实现切割优化。
    • 支持用户在 Excel 表格中输入零件和原料数据,自动生成优化切割方案。
  • 核心组件:

    • 输入表格:用户在指定工作表(如“零件”和“原料”)中输入长度和数量。
    • 优化按钮:通过 VBA 宏触发贪心法优化,基于“首次适应”(First-Fit)算法。
    • 结果表格:在新的工作表中显示切割方案,包括每根原料的零件分配和利用率。
  • 交互使用方法 :

    1. 打开 Excel 文件,启用宏(确保宏安全性设置为“启用所有宏”)。
    2. 在“零件”工作表中输入零件长度和数量(格式:A 列为长度,B 列为数量)。
    3. 在“原料”工作表中输入原料长度和数量(负值表示无限供应)。
    4. 点击工具栏中的“优化”按钮,运行 VBA 宏生成切割方案。
    5. 查看“结果”工作表,检查每根原料的零件分配和利用率。
  • 特点:

    • 轻量级,无需额外安装,适合熟悉 Excel 的用户。
    • 算法简单,适合中小规模数据(零件数量 < 1000)。
    • 支持快速修改数据和重新优化。

2. ProfileCuttingExcel(基于 VSTO)

功能和下料优化EXCEL专业版类似,插件通过 VSTO 开发,插件提供
三种计算模式:FFD,BFD,BFD2(改进算法)。
两种输出模式:型材索引和型材长度

核心算法 FFD与BFD启发式算法(采用开源项目的框架,资源分享连接可以获取压缩包),由于效率不如预期因此 FFD 和 BFD 模式在当前框架下进行了一些优化,但是发现凡是不如预期,因此根据计算原理重写了 BFD 得到了 BDF2.

三、性能对比

为了帮助用户选择适合的工具,我们对型材切割优化软件和两款 Excel 插件进行了性能对比,测试基于以下条件:

  • 测试数据:custom_items.csv,原料长度 6000 mm(无限供应),锯缝宽度 5 mm。
  • 测试环境:Windows 11,Intel Core i9-11900K 3.50GHz,32 GB RAM。
  • 指标:优化时间、原料用量、材料利用率。
名称用料量用料总长模数利用率实际利用率时间(S)
pfc2959317755800029110099.508143
pfc速2959317755800031010099.5081.2
excel2980017880000035299.3798.826.0
excel22980017880000035299.3798.820.04
专业版2980017880000035299.3798.82380

四、资源分享

所有相关资源(型材切割优化软件可执行文件、VBA 插件 Excel 文件、VSTO 插件安装包及示例数据)已打包并上传至 OneDrive 云盘,供用户下载和测试:

  • 下载链接:公众号【橡皮的CAD开发】回复【一维下料】获取
  • 内容:
    • 型材切割优化软件(ProfileCuttingPL.exe)
    • 下料优化EXCEL专业版(CuttingOptimizationPro.xlsm)
    • ProfileCuttingExcel 插件(ProfileCuttingExcel.vsto)
    • 示例数据(sample_items.csv, sample_bins.csv)
    • 使用说明文档(UserGuide.pdf)
  • 使用说明:
    • 解压后运行型材切割优化软件,需 Windows 系统支持 MFC。
    • VBA 插件需启用 Excel 宏,VSTO 插件需安装 .NET Framework。
    • 示例数据可直接导入软件或插件进行测试。

由于上传图片总是失败,可以一维下料软件分享阅读完整原文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值