欢迎光临
我们一直在努力

组件化 3C 电子 MES/ERP 协同项目全套方案

完整组件化 3C 电子 MES/ERP 协同项目全套方案(含 App.config、分层组件、WinForm、SQLite、GDI、Excel 模板、组轴多线程调度)

一、项目概述

1. 项目定位

面向3C 数码(手机 / 平板)组装产线的 WinForm 桌面协同系统,打通 ERP 物料 / BOM 与 MES 组轴工序生产流程; 解决原有代码碎片化、业务逻辑分散、线程调度杂乱问题,采用高内聚分层组件架构,所有业务抽离独立类库组件,界面只做交互,无复杂业务代码。

2. 核心业务场景

  • ERP 物料、BOM 主数据管理(固定 Excel 模板导入导出,组别 15~17)
  • 产线组轴 + 动力设备协同执行引擎(SMT / 焊接 / 组装 / 检测 4 大组轴)
  • 多模式多线程调度:单轴、组合并行、流水线联控,自动汇总线程执行结果
  • 生产作业完整生命周期:创建→调度→运行→暂停→完成→归档(贴合金蝶 ERP 单据流程)
  • GDI 原生自绘生产统计图表(产量柱状图 + 设备负载仪表盘)
  • SQLite 本地文件数据库,免服务、开箱即用
  • 全局统一App.config配置文件,数据库、线程、图表、Excel、ERP 参数统一管理
  • 3. 技术栈

    • 框架:.NET Framework 4.8 WinForms
    • ORM:SqlSugar(SQLite 本地文件库)
    • Excel:NPOI 标准固定模板
    • 并发:Task 多线程 + 事件总线解耦
    • 绘图:原生 GDI + 自定义图表控件
    • 配置:App.config 全局参数统一管理

    4. 项目分层目录

    plaintext

    3C_MES_ERP_AxisComponent/
    ├─ App.config # 全局配置文件
    ├─ Program.cs # 程序入口
    ├─ Models/ # 实体、枚举(物料/BOM/组轴/作业/调度日志)
    ├─ Components/ # 独立业务组件(核心解耦层)
    │ ├─ DbComponent/ # SQLite数据库组件
    │ ├─ EventBusComponent/ # 全局事件总线(模块解耦)
    │ ├─ AxisEngineComponent/ # 组轴执行引擎
    │ ├─ ThreadScheduleComponent/ # 多线程调度+结果组装
    │ ├─ ExcelTemplateComponent/ # 固定Excel模板导入导出
    │ ├─ GdiChartComponent/ # GDI自定义图表控件
    │ └─ ErpBomComponent/ # ERP物料BOM业务层
    └─ Forms/ # WinForm界面层(轻量化)

    二、App.config 全局统一配置文件

    所有数据库、线程、图表、Excel、ERP、组轴参数集中配置,无需修改代码切换环境

    xml

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
    </startup>

    <!– 全局自定义参数 –>
    <appSettings>
    <!– SQLite本地文件数据库配置 –>
    <add key="SqliteDbFileName" value="3C_MES_AxisLocal.db"/>
    <add key="DbLogSwitch" value="false"/>
    <add key="InitGroupStart" value="15"/>
    <add key="InitGroupEnd" value="17"/>

    <!– 组轴&多线程调度配置 –>
    <add key="MaxThreadCount" value="8"/>
    <add key="AxisTaskDelayMs" value="350"/>
    <add key="AxisLinkedGroup1" value="LINK01"/>
    <add key="AxisLinkedGroup2" value="LINK02"/>

    <!– Excel固定模板配置 –>
    <add key="MaterialTemplateName" value="3C电子物料标准模板.xlsx"/>
    <add key="BomTemplateName" value="3C电子BOM标准模板.xlsx"/>

    <!– GDI图表全局绘制参数 –>
    <add key="ChartAntiAlias" value="true"/>
    <add key="ChartBarWidth" value="26"/>
    <add key="ChartTitleFontSize" value="12"/>

    <!– 金蝶K3Cloud ERP接口参数 –>
    <add key="K3CloudUrl" value="http://192.168.60/K3Cloud"/>
    <add key="K3AcctId" value="5756960b27b1aa"/>
    <add key="K3UserName" value="Administrator"/>
    <add key="K3Password" value="888888"/>
    </appSettings>

    <!– SQLite数据库连接字符串 –>
    <connectionStrings>
    <add name="SqliteConn" connectionString="Data Source={SqliteDbFileName};Version=3;Pooling=true;Max Pool Size=10;" providerName="System.Data.SQLite"/>
    </connectionStrings>

    <!– 第三方程序集绑定重定向 –>
    <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
    <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff13" culture="neutral" version="1.0.118.0"/>
    <bindingRedirect oldVersion="0.0.0.0-1.0.118.0" newVersion="1.0.118.0"/>
    </dependentAssembly>
    </assemblyBinding>
    </runtime>
    </configuration>

    三、Models 统一实体与枚举(业务数据载体)

    csharp

    运行

    using System;
    using System.ComponentModel;
    using SqlSugar;

    namespace _3C_MES_ERP.Models
    {
    #region 全局业务枚举
    /// <summary>组轴运行状态</summary>
    public enum AxisRunState
    {
    [Description("空闲")] Idle,
    [Description("调度中")] Running,
    [Description("暂停")] Paused,
    [Description("异常停机")] Error
    }

    /// <summary>动力设备类型(3C产线)</summary>
    public enum PowerType
    {
    [Description("伺服电机")] Servo,
    [Description("气动气缸")] Cylinder,
    [Description("输送线")] Conveyor,
    [Description("检测单元")] Tester
    }

    /// <summary>生产作业生命周期(ERP标准流程)</summary>
    public enum WorkStatus
    {
    [Description("待创建")] Init,
    [Description("已调度")] Dispatched,
    [Description("执行中")] Running,
    [Description("已暂停")] Paused,
    [Description("已完成")] Completed,
    [Description("已归档")] Archived
    }

    /// <summary>调度执行模式</summary>
    public enum ScheduleMode
    {
    [Description("单轴调度")] Single,
    [Description("组合并行")] Combine,
    [Description("流水线联控")] Linked
    }
    #endregion

    #region 数据库实体
    /// <summary>ERP物料档案(组别15~17)</summary>
    [SugarTable("T_ERP_Material")]
    public class ErpMaterial
    {
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    [SugarColumn(Length = 40, IsNullable = false)]
    public string MatCode { get; set; }
    [SugarColumn(Length = 80)]
    public string MatName { get; set; }
    [SugarColumn(Length = 100)]
    public string Spec { get; set; }
    [SugarColumn(Length = 10)]
    public string Unit { get; set; }
    public decimal StockQty { get; set; }
    public int GroupNo { get; set; } // 固定15/16/17
    public string MatType { get; set; }
    public DateTime CreateTime { get; set; } = DateTime.Now;
    }

    /// <summary>ERP BOM结构单</summary>
    [SugarTable("T_ERP_BOM")]
    public class ErpBom
    {
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public string BomNo { get; set; }
    public string ParentMatCode { get; set; }
    public string ChildMatCode { get; set; }
    public decimal UseQty { get; set; }
    public string BomVersion { get; set; }
    public int GroupNo { get; set; }
    }

    /// <summary>产线组轴</summary>
    [SugarTable("T_AxisGroup")]
    public class AxisGroup
    {
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public string AxisCode { get; set; } // AX01~AX04
    public string AxisName { get; set; }
    public AxisRunState RunState { get; set; }
    public string BindProcess { get; set; }
    public string LinkedGroup { get; set; } // 联控分组
    }

    /// <summary>动力执行设备</summary>
    [SugarTable("T_PowerDevice")]
    public class PowerDevice
    {
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public string PowerCode { get; set; }
    public string PowerName { get; set; }
    public PowerType DeviceType { get; set; }
    public string BindAxisCode { get; set; }
    public decimal LoadRate { get; set; }
    public string DeviceStatus { get; set; }
    }

    /// <summary>生产作业单(对接ERP生产任务)</summary>
    [SugarTable("T_WorkOrder")]
    public class WorkOrder
    {
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public string WorkNo { get; set; }
    public string RelBomNo { get; set; }
    public int PlanQty { get; set; }
    public int CompleteQty { get; set; }
    public WorkStatus WorkStatus { get; set; }
    public string BindAxisCode { get; set; }
    public string BindPowerCode { get; set; }
    public DateTime CreateTime { get; set; } = DateTime.Now;
    public DateTime? FinishTime { get; set; }
    }

    /// <summary>调度执行日志(多线程结果持久化载体)</summary>
    [SugarTable("T_ScheduleLog")]
    public class ScheduleLog
    {
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public ScheduleMode ScheduleMode { get; set; }
    public string TargetAxis { get; set; }
    public string Operator { get; set; }
    public DateTime OperTime { get; set; } = DateTime.Now;
    public string FullResult { get; set; } // 组装后的完整线程执行结果
    }

    /// <summary>GDI图表统计DTO</summary>
    public class ChartDataDto
    {
    public string WorkName { get; set; }
    public decimal PlanNum { get; set; }
    public decimal ActualNum { get; set; }
    public decimal LoadAvg { get; set; }
    }
    #endregion
    }

    四、分层独立组件代码(核心业务解耦,消除零散代码)

    组件 1:DbComponent 本地 SQLite 数据库组件(读取 App.config)

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.IO;
    using SqlSugar;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Components.DbComponent
    {
    public static class LocalSqlDb
    {
    private static SqlSugarClient _dbClient;
    private static readonly string _dbFile;
    private static readonly int _startGroup;
    private static readonly int _endGroup;

    static LocalSqlDb()
    {
    // 从App.config读取数据库配置
    _dbFile = ConfigurationManager.AppSettings["SqliteDbFileName"];
    _start = int.Parse(ConfigurationManager.AppSettings["InitGroupStart"]);
    _end = int.Parse(ConfigurationManager.AppSettings["InitGroupEnd"]);
    InitSqlSugarClient();
    CreateTableAndInitData();
    }

    /// 初始化SqlSugar连接
    private static void InitSqlSugarClient()
    {
    string connStr = ConfigurationManager.ConnectionStrings["SqliteConn"].ConnectionString.Replace("{SqliteDbFileName}", _dbFile);
    _dbClient = new SqlSugarClient(new ConnectionConfig
    {
    ConnectionString = connStr,
    DbType = DbType.Sqlite,
    IsAutoCloseConnection = true,
    InitKeyType = InitKeyType.Attribute,
    AopEvents = new AopEvent
    {
    OnLogExecuting = (sql, pars) =>
    {
    if (ConfigurationManager.AppSettings["DbLogSwitch"] == "true")
    Console.WriteLine(sql);
    }
    }
    });
    }

    /// 自动建表 + 初始化15~17组3C测试数据
    private static void CreateTableAndInitData()
    {
    _dbClient.CodeFirst.InitTables(
    typeof(ErpMaterial), typeof(ErpBom), typeof(AxisGroup),
    typeof(PowerDevice), typeof(WorkOrder), typeof(ScheduleLog));

    if (!_dbClient.Queryable<ErpMaterial>().Any()) InitMaterial();
    if (!_dbClient.Queryable<ErpBom>().Any()) InitBom();
    if (!_dbClient.Queryable<AxisGroup>().Any()) InitAxis();
    if (!_dbClient.Queryable<PowerDevice>().Any()) InitPower();
    }

    #region 预置15~17组业务数据
    private static void InitMaterial()
    {
    Random rand = new Random();
    List<ErpMaterial> list = new List<ErpMaterial>();
    string[] names = {PCB主板,"芯片","电容","摄像头","电池","外壳"};
    string[] types = {"原材料","半成品","成品"};
    string[] unit = {"PCS","片","套"};
    for (int g = _start; g <= _end; g++)
    {
    for (int i = 0; i < 3; i++)
    {
    list.Add(new ErpMaterial
    {
    MatCode = $"MAT{g:D2}{i:D2}",
    MatName = $"{names[rand.Next(names)]}_{g}组",
    Spec = $"V{rand.Next(1)}.{rand.Next(9)}",
    Unit = unit[rand.Next(unit)],
    StockQty = rand.Next(200,12000),
    GroupNo = g,
    MatType = types[rand.Next(types)]
    });
    }
    }
    _dbClient.Insertable(list).ExecuteCommand();
    }

    private static void InitBom()
    {
    Random rand = new Random();
    List<ErpBom> list = new List<ErpBom>();
    for (int g = _start; g <= _end; g++)
    {
    list.Add(new ErpBom
    {
    BomNo = $"BOM{g:D2}01",
    ParentMatCode = $"MAT{g:D2}00",
    ChildMatCode = $"MAT{g:D2}01",
    UseQty = Math.Round((decimal)rand.Next(1,5)/10,2),
    BomVersion = "V1.0",
    GroupNo = g
    });
    }
    _dbClient.Insertable(list).ExecuteCommand();
    }

    private static void InitAxis()
    {
    List<AxisGroup> list = new List<AxisGroup>()
    {
    new AxisGroup{AxisCode="AX01",AxisName="SMT贴片",RunState=AxisRunState.Idle,BindProcess="上料、贴片",LinkedGroup=ConfigurationManager.AppSettings["AxisLinkedGroup1"]},
    new AxisGroup{AxisCode="AX02",AxisName="高温焊接",RunState=AxisRunState.Idle,BindProcess="回流焊",LinkedGroup=ConfigurationManager.AppSettings["AxisLinkedGroup1"]},
    new AxisGroup{AxisCode="AX03",AxisName="整机组装",RunState=AxisRunState.Idle,BindProcess="锁螺丝、贴合",LinkedGroup=ConfigurationManager.AppSettings["AxisLinkedGroup2"]},
    new AxisGroup{AxisCode="AX04",AxisName="功能检测",RunState=AxisRunState.Idle,BindProcess="通电检测",LinkedGroup=ConfigurationManager.AppSettings["AxisLinkedGroup2"]}
    };
    _dbClient.Insertable(list).ExecuteCommand();
    }

    private static void InitPower()
    {
    Random rand = new Random();
    List<PowerDevice> list = new List<PowerDevice>();
    string[] axisArr = {"AX01","AX02","AX03","AX04"};
    var powerTypes = Enum.GetValues(typeof(PowerType)).Cast<PowerType>();
    for (int i = 1; i <= _end – _start + 1; i++)
    {
    list.Add(new PowerDevice
    {
    PowerCode = $"PU{i:D2}",
    PowerName = $"动力单元{i}",
    DeviceType = powerTypes.ElementAt(rand.Next(powerTypes.Count())),
    BindAxisCode = axisArr[rand.Next(axisArr.Length)],
    LoadRate = rand.Next(10,80),
    DeviceStatus = "正常"
    });
    }
    _dbClient.Insertable(list).ExecuteCommand();
    }
    #endregion

    // 通用CRUD封装
    public static SqlSugarClient GetDb() => _dbClient;
    public static bool Insert<T>(T entity) => _dbClient.Insertable(entity).ExecuteCommand() > 0;
    public static bool Update<T>(T entity) => _dbClient.Updateable(entity).ExecuteCommand() > 0;
    public static List<T> QueryAll<T>() => _dbClient.Queryable<T>().ToList();
    public static T QueryById<T>(object id) => _dbClient.Queryable<T>().InSingle(id);
    }
    }

    组件 2:EventBusComponent 全局事件总线(解耦模块,消除跨窗体零散回调)

    csharp

    运行

    using System;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Components.EventBusComponent
    {
    public static class GlobalEventBus
    {
    #region 全局委托定义
    public delegate void LogPrintDel(string logInfo);
    public delegate void UIRefreshDel();
    public delegate AxisMsgDel(string axisCode, string info);
    public delegate WorkStateDel(WorkOrder order, WorkStatus status);
    public delegate ScheduleResultDel(string fullResult);
    #endregion

    #region 全局订阅事件
    public static event LogPrintDel OnLog;
    public static event UIRefreshDel OnUiRefresh;
    public static event AxisMsgDel OnAxisNotify;
    public static event WorkStateDel OnWorkChange;
    public static event ScheduleResultDel OnScheduleComplete;
    #endregion

    // 输出日志
    public static void PublishLog(string msg)
    {
    string log = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | {msg}";
    OnLog?.Invoke(log);
    }
    // 通知界面刷新
    public static void PublishRefresh() => OnUiRefresh?.Invoke();
    // 推送组轴执行消息
    public static void PublishAxisMsg(string axis, string info)
    {
    PublishLog($"【组轴{axis}】{info}");
    OnAxisNotify?.Invoke(axis, info);
    }
    // 作业状态变更推送
    public static void PublishWorkState(WorkOrder order, WorkStatus status)
    {
    PublishLog($"作业{order.WorkNo} 状态:{status}");
    OnWorkChange?.Invoke(order, status);
    }
    // 推送多线程组装完整调度结果
    public static void PublishScheduleResult(string result)
    {
    OnScheduleComplete?.Invoke(result);
    }
    }
    }

    组件 3:AxisEngineComponent 组轴执行引擎(统一收拢零散工序执行代码)

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Threading;
    using _3C_MES_ERP.Components.DbComponent;
    using _3C_MES_ERP.Components.EventBusComponent;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Components.AxisEngineComponent
    {
    public class AxisExecuteEngine
    {
    private readonly int _taskDelay;
    private readonly Dictionary<string, List<string>> _axisTaskMap = new();
    private readonly Dictionary<string, AxisRunState> _axisStateMap = new();
    private readonly Dictionary<string, List<string>> _linkGroupMap = new();

    public AxisExecuteEngine()
    {
    _taskDelay = int.Parse(ConfigurationManager.AppSettings["AxisTaskDelayMs"]);
    LoadAxisConfigData();
    }

    /// 加载组轴、联控分组配置
    private void LoadAxisConfigData()
    {
    var axisList = LocalSqlDb.QueryAll<AxisGroup>();
    foreach (var axis in axisList)
    {
    _axisStateMap[axis.AxisCode] = axis.RunState;
    _axisTaskMap[axis.AxisCode] = axis.BindProcess.Split('、').ToList();
    }
    // 构建联控分组映射
    var groupQuery = axisList.GroupBy(g => g.LinkedGroup);
    foreach (var g in groupQuery)
    _linkGroupMap[g.Key] = g.Select(x => x.AxisCode).ToList();
    }

    /// 单组轴同步执行工序,返回完整执行文本结果
    public string RunSingleAxis(string axisCode)
    {
    if (!_axisStateMap.ContainsKey(axisCode) || _axisStateMap[axisCode] != AxisRunState.Idle)
    return $"组轴{axisCode}当前状态不可执行";

    _axisStateMap[axisCode] = AxisRunState.Running;
    var powerList = LocalSqlDb.QueryAll<PowerDevice>().Where(p => p.BindAxisCode == axisCode).ToList();
    var taskList = _axisTaskMap[axisCode];
    string resultText = $"====组轴{axisCode}执行开始====\\r\\n";

    foreach (var proc in taskList)
    {
    if (_axisStateMap[axisCode] == AxisRunState.Paused)
    {
    result += $"工序{proc}:手动暂停\\r\\n";
    continue;
    }
    // 动力设备负载上升
    foreach (var power in powerList)
    {
    power.LoadRate += 8;
    if (power.LoadRate > 90) power.DeviceStatus = "过载预警";
    LocalSqlDb.Update(power);
    }
    Thread.Sleep(_taskDelay);
    resultText += $"工序{proc}执行完成\\r\\n";
    GlobalEventBus.PublishAxisMsg(axisCode, $"工序{proc}完成");
    }

    // 执行完毕负载回落
    foreach (var power in powerList)
    {
    power.LoadRate = Math.Max(0, power.LoadRate – 15);
    power.DeviceStatus = "正常";
    LocalSqlDb.Update(power);
    }
    _axisStateMap[axisCode] = AxisRunState.Idle;
    resultText += $"====组轴{axisCode}全部工序结束====\\r\\n";
    return resultText;
    }

    /// 暂停指定组轴
    public void PauseSingleAxis(string axisCode)
    {
    if (_axisStateMap.ContainsKey(axisCode) && _axisStateMap[axisCode] == AxisRunState.Running)
    _axisStateMap[axisCode] = AxisRunState.Paused;
    }

    /// 紧急停止整个联控分组
    public void StopLinkedGroup(string linkCode)
    {
    if (!_linkGroupMap.TryGetValue(linkCode, out var axisArr)) return;
    foreach (var ax in axisArr)
    _axisStateMap[ax] = AxisRunState.Error;
    GlobalEventBus.PublishLog($"联控组{linkCode}全部设备紧急停机");
    }

    /// 重置所有组轴为空闲
    public void ResetAllAxis()
    {
    foreach (var key in _axisStateMap.Keys)
    _axisStateMap[key] = AxisRunState.Idle;
    GlobalEventBus.PublishLog("所有组状态重置完成");
    }

    public List<string> GetAllAxisCode() => _axisStateMap.Keys.ToList();
    public List<string> GetAllLinkGroup() => _linkGroupMap.Keys.ToList();
    }
    }

    组件 4:ThreadScheduleComponent 多线程调度 + 结果组装(收拢零散 Task 代码)

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using _3C_MES_ERP.Components.AxisEngineComponent;
    using _3C_MES_ERP.Components.DbComponent;
    using _3C_MES_ERP.Components.EventBusComponent;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Components.ThreadScheduleComponent
    {
    public class MultiThreadSchedule
    {
    private readonly AxisExecuteEngine _axisEngine;
    private readonly List<string> _resultBuffer = new();

    public MultiThreadSchedule(AxisExecuteEngine engine)
    {
    _axisEngine = engine;
    }

    /// 单线程调度
    public async Task<string> RunSingleTask(string axisCode, string operatorName)
    {
    _resultBuffer.Clear();
    string singleRes = await Task.Run(() => _axisEngine.RunSingleAxis(axisCode));
    _resultBuffer.Add(singleRes);
    string fullText = string.Join("\\r\\n", _resultBuffer);
    SaveScheduleLog(ScheduleMode.Single, axisCode, operatorName, fullText);
    GlobalEventBus.PublishScheduleResult(fullText);
    return fullText;
    }

    /// 多轴组合并行调度,自动组装全部线程返回结果
    public async Task<string> RunCombineTask(List<string> axisList, string operatorName)
    {
    _resultBuffer.Clear();
    List<Task<string>> taskGroup = new List<Task<string>>();
    foreach (var ax in axisList)
    taskGroup.Add(Task.Run(() => _axisEngine.RunSingleAxis(ax)));
    await Task.WhenAll(taskGroup);
    // 统一组装所有线程执行输出
    string combineResult = "【组合并行调度汇总】\\r\\n" + string.Join("\\r\\n—————–\\r\\n", taskGroup.Select(t => t.Result));
    SaveScheduleLog(ScheduleMode.Combine, string.Join("、", axisList), operatorName, combineResult);
    GlobalEventBus.PublishScheduleResult(combineResult);
    return combineResult;
    }

    /// 流水线联控调度(顺序执行,单异常整组停机)
    public async Task<string> RunLinkedTask(string linkGroup, string operatorName)
    {
    _resultBuffer.Clear();
    var axisArr = _axisEngine.GetAllLinkGroup().Contains(linkGroup) ? _axisEngine.GetAllLinkGroup()[linkGroup] : new List<string>();
    if (!axisArr.Any()) return "该联控分组不存在";
    string totalText = $"【联控流水线{linkGroup}】\\r\\n";
    foreach (var ax in axisArr)
    {
    string res = await Task.Run(() => _axisEngine.RunSingleAxis(ax));
    totalText += res + "\\r\\n";
    if (res.Contains("过载预警") || res.Contains("暂停"))
    {
    _axisEngine.StopLinkedGroup(linkGroup);
    totalText += ">>>> 设备异常,流水线整体终止 <<<<\\r\\n";
    break;
    }
    }
    SaveScheduleLog(ScheduleMode.Linked, linkGroup, operatorName, totalText);
    GlobalEventBus.PublishScheduleResult(totalText);
    return totalText;
    }

    /// 全部调度停止
    public void StopAllSchedule() => _axisEngine.ResetAllAxis();

    /// 调度日志入库(组装完整结果持久化)
    private void SaveScheduleLog(ScheduleMode mode, string target, string user, string fullResult)
    {
    var log = new ScheduleLog
    {
    ScheduleMode = mode,
    TargetAxis = target,
    Operator = user,
    FullResult = fullResult
    };
    LocalSqlDb.Insert(log);
    GlobalEventBus.PublishRefresh();
    }
    }
    }

    组件 5:ExcelTemplateComponent 固定模板导入导出(标准化 Excel 逻辑收拢)

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Windows.Forms;
    using NPOI.HSSF.UserModel;
    using NPOI.SS.UserModel;
    using NPOI.XSSF.UserModel;
    using _3C_MES_ERP.Components.DbComponent;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Components.ExcelTemplateComponent
    {
    public static class ExcelFixedTemplate
    {
    // 两套标准固定表头(不可修改)
    private static readonly List<string> MaterialHeader = new()
    { "物料编码","物料名称","规格型号","单位","库存数量","组别(15/16/17)","物料类型" };
    private static readonly List<string> BomHeader = new()
    { "BOM单号","父物料编码","子物料编码","单台用量","版本","组别(15/16/17)" };

    #region 下载标准空白模板
    public static void DownloadMaterialTemplate() => CreateTemplate(MaterialHeader, ConfigurationManager.AppSettings["MaterialTemplateName"]);
    public static void DownloadBomTemplate() => CreateTemplate(BomHeader, ConfigurationManager.AppSettings["BomTemplateName"]);

    private static void CreateTemplate(List<string> header, string fileName)
    {
    using SaveFileDialog sfd = new SaveFileDialog();
    s.Filter = "Excel(*.xlsx)|*.xlsx";
    s.FileName = fileName;
    if (s.ShowDialog() != DialogResult) return;
    IWorkbook wb = new XSSFWorkbook();
    ISheet sheet = wb.CreateSheet("标准模板");
    IRow hRow = sheet.CreateRow(0);
    for (int i = 0; i < header.Count; i++)
    {
    hRow.CreateCell(i).SetCellValue(header[i]);
    sheet.SetColumnWidth(i, 22 * 256);
    }
    // 示例行
    IRow demoRow = sheet.CreateRow(1);
    header.ForEach((h, idx) => demoRow.CreateCell(idx).SetCellValue($"示例_{h}"));
    using FileStream fs = new FileStream(sfd.FileName, FileMode.Create);
    wb.Write(fs);
    MessageBox.Show("标准模板下载完成");
    }
    #endregion

    #region Excel导入(强制校验组别15~17)
    public static (bool Success, string Msg, List<ErpMaterial> ImportMaterial)
    {
    List<ErpMaterial> dataList = new List<ErpMaterial>();
    using OpenFileDialog ofd = new OpenFileDialog();
    of.Filter = "*.xlsx;*.xls";
    if (of.ShowDialog() != DialogResult) return (false, "已取消", dataList);
    try
    {
    IWorkbook wb;
    using FileStream fs = new FileStream(of.FileName, FileMode.Open);
    wb = of.FileName.EndsWith(".xlsx") ? new XSSFWorkbook(fs) : new HSSFWorkbook(fs);
    ISheet sheet = wb.GetSheetAt(0);
    for (int r = 1; r <= sheet.LastRowNum; r++)
    {
    IRow row = sheet.GetRow(r);
    if (row == null) continue;
    int group = int.TryParse(GetCell(row, 5), out int g) ? g : 0;
    if (group < 15 || group > 17)
    return (false, $"第{r+1}行组别必须15/16/17", dataList);
    dataList.Add(new ErpMaterial
    {
    MatCode = GetCell(row, 0),
    MatName = GetCell(row, 1),
    Spec = GetCell(row, 2),
    Unit = GetCell(row, 3),
    StockQty = decimal.TryParse(GetCell(row, 4), out var q) ? q : 0,
    GroupNo = group,
    MatType = GetCell(row, 6)
    });
    }
    return (true, $"读取{dataList}条物料", dataList);
    }
    catch (Exception ex)
    {
    return (false, $"解析异常:{ex.Message}", dataList);
    }
    }
    #endregion

    #region 数据导出
    public static void ExportMaterial() => ExportData(MaterialHeader, LocalSqlDb.QueryAll<ErpMaterial>, m =>
    new object[] { m.MatCode, m.MatName, m.Spec, m.Unit, m.StockQty, m.GroupNo, m.MatType });
    public static void ExportBom() => ExportData(BomHeader, LocalSqlDb.QueryAll<ErpBom>, b =>
    new object[] { b.BomNo, b.ParentMatCode, b.ChildMatCode, b.UseQty, b.BomVersion, b.GroupNo });

    private static void ExportData<T>(List<string> header, List<T> source, Func<T, object[]> rowFunc)
    {
    if (source.Count == 0) { MessageBox.Show("无数据"); return; }
    using SaveFileDialog sfd = new SaveFileDialog();
    s.Filter = "*.xlsx";
    if (s.ShowDialog() != DialogResult) return;
    IWorkbook wb = new XSSFWorkbook();
    ISheet sheet = wb.CreateSheet("导出数据");
    IRow hRow = sheet.CreateRow(0);
    for (int i = 0; i < header.Count; i++) hRow.CreateCell(i).SetCellValue(header[i]);
    for (int i = 0; i < source.Count; i++)
    {
    IRow row = sheet.CreateRow(i + 1);
    var cells = rowFunc(source[i]);
    for (int c = 0; c < cells.Length; c++)
    row.CreateCell(c, cells[c]?.ToString() ?? "");
    }
    using FileStream fs = new FileStream(sfd.FileMode.Create);
    wb.Write(fs);
    MessageBox.Show("导出完成");
    }
    #endregion

    private static string GetCell(IRow row, int idx)
    {
    var cell = row.GetCell(idx);
    return cell == null ? "" : cell.ToString().Trim();
    }
    }
    }

    组件 6:GdiChartComponent GDI 自绘图表组件(独立绘图逻辑,无窗体混杂代码)

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Windows.Forms;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Components.GdiChartComponent
    {
    public class ProductionGdiChart : Control
    {
    private List<ChartDataDto> _chartData = new();
    private readonly bool _antiAlias;
    private readonly int _barWidth;
    private readonly int _fontSize;

    public ProductionGdiChart()
    {
    // 读取App.config绘图参数
    _antiAlias = bool.Parse(ConfigurationManager.AppSettings["ChartAntiAlias"]);
    _barWidth = int.Parse(ConfigurationManager.AppSettings["ChartBarWidth"]);
    _fontSize = int.Parse(ConfigurationManager.AppSettings["ChartTitleFontSize"]);
    DoubleBuffered = true;
    SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }

    public List<ChartDataDto> DataSource
    {
    set { _chartData = value; Invalidate(); }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
    Graphics g = e.Graphics;
    if (_antiAlias) g.SmoothingMode = SmoothingMode.AntiAlias;
    g.Clear(Color.White);
    if (_chart.Count == 0)
    {
    g.DrawString("暂无生产统计数据", new Font("微软雅黑", _fontSize), Brushes.Gray, Width / 2 – 60, Height / 2);
    return;
    }
    DrawTitle(g);
    DrawAxis(g);
    DrawBarChart(g);
    DrawLoadGauge(g);
    }

    private void DrawTitle(Graphics g)
    {
    Font font = new Font("微软雅黑", _fontSize, FontStyle.Bold);
    string title = "3C电子产线产量&设备负载报表";
    var size = g.MeasureString(title, font);
    g.DrawString(title, font, Brushes.Black, (Width – size.Width) / 2, 8);
    font.Dispose();
    }

    private void DrawAxis(Graphics g)
    {
    int pad = 40;
    Pen gridPen = new Pen(Color.LightGray, 1) { DashStyle = DashStyle.Dash };
    g.DrawLine(Pens.Black, pad, pad, pad, Height – pad);
    g.DrawLine(Pens.Black, pad, Height – pad, Width – pad, Height – pad);
    // Y刻度网格
    for (int i = 1; i <= 5; i++)
    {
    int y = Height – pad – (Height – pad) / 5 * i;
    g.DrawLine(grid, pad, y, Width – pad, y);
    g.DrawString($"{i * 20}%", new Font("微软雅黑", 9), Brushes.Gray, 10, y – 8);
    }
    gridPen.Dispose();
    }

    private void DrawBarChart(Graphics g)
    {
    int pad = 40;
    int x = pad + 10;
    decimal maxVal = Math.Max(_chart.Max(d => d.PlanNum), _chart.Max(d => d.ActualNum));
    float scale = (Height – pad * 2) / (float)maxVal;
    foreach (var item in _chartData)
    {
    float hPlan = (float)item.PlanNum * scale;
    g.FillRectangle(Brushes.RoyalBlue, x, Height – pad – hPlan, _barWidth, hPlan);
    float hAct = (float)item.ActualNum * scale;
    g.FillRectangle(Brushes.SeaGreen, x + _barWidth + 6, Height – pad – hAct, _barWidth, hAct);
    g.DrawString(item.WorkName, new Font("微软雅黑", 8), Brushes.Black, x, Height – pad + 5);
    x += _barWidth * 2 + 30;
    }
    }

    private void DrawLoadGauge(Graphics g)
    {
    decimal avgLoad = _chartData.Average(d => d.LoadAvg);
    int cx = Width – 130;
    int cy = Height / 2;
    int r = 80;
    using Pen bgPen = new Pen(Color.LightGray, 16) { StartCap = LineCap.Round, EndCap = LineCap.Round };
    g.DrawArc(bgPen, cx – r, cy – r, r * 2, r * 2, 135, 270);
    using Pen forePen = new Pen(Color.OrangeRed, 16);
    g.DrawArc(forePen, cx – r + 8, cy – r + 8, (r – 8) * 2, (r – 8) * 2, 135, (float)(270 * avgLoad / 100));
    g.DrawString($"平均负载\\n{avgLoad:F1}%", new Font("微软雅黑", 10), Brushes.DarkRed, cx – 40, cy – 25);
    }

    protected override void Dispose(bool dispos)
    {
    base.Dispose(dis);
    }
    }
    }

    组件 7:ErpBomComponent ERP 物料 BOM 作业业务层(收拢零散单据逻辑)

    csharp

    运行

    using System.Collections.Generic;
    using System.Linq;
    using _3C_MES_ERP.Components.DbComponent;
    using _3C_MES_ERP.Components.EventBusComponent;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Components.ErpBomComponent
    {
    public class ErpBusinessService
    {
    #region 物料CRUD
    public List<ErpMaterial> GetAllMaterial() => LocalSqlDb.QueryAll<ErpMaterial>();
    public bool AddMaterial(ErpMaterial m) => LocalSqlDb.Insert(m);
    public bool UpdateMaterial(ErpMaterial m) => LocalSqlDb.Update(m);
    #endregion

    #region BOM查询
    public List<ErpBom> GetAllBom() => LocalSqlDb.QueryAll<ErpBom>();
    public List<ErpBom> GetBomByGroup(int groupNo) => LocalSqlDb.QueryAll<ErpBom>().Where(b => b.GroupNo == group).ToList();
    #endregion

    #region 生产作业单生命周期(ERP标准单据流程)
    /// 状态流转校验,禁止非法跨状态操作
    private bool CheckWorkFlow(WorkOrder curr, WorkOrder targetStatus)
    {
    return (curr.WorkStatus, targetStatus) switch
    {
    (WorkStatus.Init, WorkStatus.Dispatched) => true,
    (WorkStatus.Dispatched, WorkStatus.Running) => true,
    (WorkStatus.Running, WorkStatus.Paused) => true,
    (WorkStatus.Paused, WorkStatus.Running) => true,
    (WorkStatus.Running, WorkStatus.Completed) => true,
    (WorkStatus.Paused, WorkStatus.Completed) => true,
    (WorkStatus.Completed, WorkStatus.Archived) => true,
    _ => false
    };
    }

    /// 创建ERP生产作业单
    public WorkOrder CreateWork(string bomNo, string axisCode, int planQty)
    {
    WorkOrder wo = new WorkOrder
    {
    WorkNo = $"WO{DateTime.Now:yyyyMMddHHmmss}",
    RelBomNo = bomNo,
    PlanQty = planQty,
    CompleteQty = 0,
    WorkStatus = WorkStatus.Init,
    BindAxisCode = axisCode
    };
    LocalSqlDb.Insert(wo);
    GlobalEventBus.PublishWorkState(wo, WorkStatus.Init);
    GlobalEventBus.PublishRefresh();
    return wo;
    }

    /// 调度作业
    public bool DispatchWork(WorkOrder wo)
    {
    if (!CheckWorkFlow(wo, WorkStatus.Dispatched)) return false;
    wo.WorkStatus = WorkStatus.Dispatched;
    LocalSqlDb.Update(wo);
    GlobalEventBus.PublishWorkState(wo, WorkStatus.Dispatched);
    return true;
    }

    /// 暂停作业
    public bool PauseWork(WorkOrder wo)
    {
    if (!CheckWorkFlow(wo, WorkStatus.Paused)) return false;
    wo.WorkStatus = WorkStatus.Paused;
    LocalSqlDb.Update(wo);
    GlobalEventBus.PublishWorkState(wo, WorkStatus.Paused);
    return true;
    }

    /// 完成作业上报产量
    public bool FinishWork(WorkOrder wo, int completeQty)
    {
    if (!CheckWorkFlow(wo, WorkStatus.Completed) || completeQty > wo.PlanQty) return false;
    wo.CompleteQty = completeQty;
    wo.WorkStatus = WorkStatus.Completed;
    wo.FinishTime = DateTime.Now;
    LocalSqlDb.Update(wo);
    GlobalEventBus.PublishWorkState(wo, WorkStatus.Completed);
    return true;
    }

    /// ERP单据归档
    public bool ArchiveWork(WorkOrder wo)
    {
    if (!CheckWorkFlow(wo, WorkStatus.Archived)) return false;
    wo.WorkStatus = WorkStatus.Archived;
    LocalSqlDb.Update(wo);
    GlobalEventBus.PublishWorkState(wo, WorkStatus.Archived);
    return true;
    }

    public List<WorkOrder> GetAllWorkOrder() => LocalSqlDb.QueryAll<WorkOrder>().OrderByDesc(w => w.CreateTime).ToList();
    #endregion
    }
    }

    五、Forms WinForm 界面层(轻量化,无复杂业务,仅调用组件)

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using _3C_MES_ERP.Components.AxisEngineComponent;
    using _3C_MES_ERP.Components.EventBusComponent;
    using _3C_MES_ERP.Components.ExcelTemplateComponent;
    using _3C_MES_ERP.Components.GdiChartComponent;
    using _3C_MES_ERP.Components.ErpBomComponent;
    using _3C_MES_ERP.Components.ThreadScheduleComponent;
    using _3C_MES_ERP.Models;

    namespace _3C_MES_ERP.Forms
    {
    public partial class MainCooperForm : Form
    {
    // 全局组件实例(界面仅调用,不写业务逻辑)
    private readonly AxisExecuteEngine _axisEngine;
    private readonly MultiThreadSchedule _schedule;
    private readonly ErpBusinessService _erpBiz;
    private readonly ProductionGdiChart _chart;

    public MainCooperForm()
    {
    // 实例化所有底层业务组件
    _axisEngine = new AxisExecuteEngine();
    _schedule = new MultiThreadSchedule(_axisEngine);
    _erpBiz = new ErpBusinessService();
    _chart = new ProductionGdiChart { Dock = DockStyle.Fill };

    InitializeComponent();
    InitMainUI();
    BindEventBusSubscribe();
    LoadAllTableData();
    }

    /// 初始化顶部菜单、下拉框UI
    private void InitMainUI()
    {
    Text = "3C电子MES/ERP物料BOM协同平台 | 组轴多线程调度";
    WindowState = FormWindowState.Maximized;
    // 顶部菜单
    MenuStrip menu = new MenuStrip { Dock = DockStyle.Top };
    // Excel菜单
    ToolStripMenuItem excelMenu = new ToolStripMenuItem("Excel标准模板");
    excel.DropDownItems.Add("下载物料模板", null, (s, e) => ExcelFixedTemplate.DownloadMaterialTemplate());
    excel.DropDownItems.Add("下载BOM模板", null, (s, e) => ExcelFixedTemplate.DownloadBomTemplate());
    ToolStripMenuItem dataMenu = new ToolStripMenuItem("导入导出");
    data.DropDownItems.Add("导入物料", null, ImportMatClick);
    data.DropDownItems.Add("全部导出", null, (s, e) => { ExcelFixedTemplate.ExportMaterial(); ExcelFixedTemplate.ExportBom(); });
    // 调度菜单
    ToolStripMenuItem schMenu = new ToolStripMenuItem("调度指令中心");
    sch.DropDownItems.Add("单轴调度", null, async (s, e) => await RunSingle());
    sch.DropDownItems.Add("组合并行", null, async (s, e) => await RunCombine());
    sch.DropDownItems.Add("流水线联控", null, async (s, e) => await RunLink());
    sch.DropDownItems.Add("全部停止", null, (s, e) => _schedule.StopAllSchedule());
    menu.Items.Add(excelMenu);
    menu.Items.Add(dataMenu);
    menu.Items.Add(sch);
    Controls.Add(menu);

    // 下拉框绑定组轴、联控分组
    cboSingleAxis.Items.AddRange(_axisEngine.GetAllAxisCode().ToArray());
    clbCombine.Items.AddRange(_axisEngine.GetAllAxisCode().ToArray());
    cboLinkGroup.Items.AddRange(_axisEngine.GetAllLinkGroup().ToArray());
    if (cboSingleAxis.Items.Count > 0) cboSingleAxis.SelectedIndex = 0;
    // 图表容器
    panelChart.Controls.Add(_chart);
    }

    /// 订阅全局事件总线(日志、自动刷新、调度结果弹窗)
    private void BindEventBusSubscribe()
    {
    GlobalEventBus.OnLog += log =>
    {
    if (txtLog.InvokeRequired) txtLog.Invoke(() => txtLog.AppendText(log + "\\r\\n"));
    else txtLog.AppendText(log + "\\r\\n");
    };
    GlobalEventBus.OnUiRefresh += LoadAllTableData;
    GlobalEventBus.OnScheduleComplete += res => MessageBox.Show("调度执行完成:\\r\\n" + res);
    }

    /// 刷新所有物料/BOM/作业表格与图表
    private void LoadAllTableData()
    {
    dgvMaterial.DataSource = _erpBiz.GetAllMaterial();
    dgvBom.DataSource = _erpBiz.GetAllBom();
    dgvWork.DataSource = _erpBiz.GetAllWorkOrder();
    // 图表数据源
    List<ChartDataDto> chartSource = new List<ChartDataDto>();
    foreach (var wo in _erpBiz.GetAllWorkOrder())
    {
    chartSource.Add(new ChartDataDto
    {
    WorkName = wo.WorkNo,
    PlanNum = wo.PlanQty,
    ActualNum = wo.CompleteQty,
    LoadAvg = new Random().Next(10, 85)
    });
    }
    _chart.DataSource = chartSource;
    }

    #region 三种调度按钮逻辑(仅调用组件,无业务代码)
    private async Task RunSingle()
    {
    if (cboSingleAxis.SelectedItem == null) return;
    await _schedule.RunSingleTask(cboSingle.Text, "系统管理员");
    }
    private async Task RunCombine()
    {
    List<string> selAxis = new List<string>();
    for (int i = 0; i < clbCombine.Items.Count; i++)
    if (clbCombine.GetItemChecked(i)) selAxis.Add(clbCombine.Items[i].ToString());
    if (selAxis.Count == 0) { MessageBox.Show("请勾选组轴"); return; }
    await _schedule.RunCombineTask(selAxis, "系统管理员");
    }
    private async Task RunLink()
    {
    if (cboLinkGroup.SelectedItem == null) return;
    await _schedule.RunLinkedTask(cboLink.Text, "系统管理员");
    }
    #endregion

    #region ERP作业生命周期按钮
    private void btnCreateWork_Click(object sender, EventArgs e)
    {
    if (!int.TryParse(txtPlanQty.Text, out int qty))
    {
    MessageBox.Show("请输入合法计划产量");
    return;
    }
    var work = _erpBiz.CreateWork(txtBomNo.Text.Trim(), cboSingle.Text, qty);
    MessageBox.Show($"作业创建成功:{work.WorkNo}");
    }
    private void btnPauseWork_Click(object sender, EventArgs e)
    {
    var sel = GetSelectWork();
    if (sel == null) return;
    bool ok = _erpBiz.PauseWork(sel);
    MessageBox.Show(ok ? "暂停成功" : "当前状态禁止操作");
    }
    private void btnArchiveWork_Click(object sender, EventArgs e)
    {
    var sel = GetSelectWork();
    if (sel == null) return;
    bool ok = _erpBiz.ArchiveWork(sel);
    MessageBox.Show(ok ? "ERP单据归档完成" : "状态不支持归档");
    }
    private WorkOrder GetSelectWork()
    {
    if (dgvWork.SelectedRows.Count == 0)
    {
    MessageBox.Show("请选中作业行");
    return null;
    }
    return dgvWork.SelectedRows[0].DataBoundItem as WorkOrder;
    }
    #endregion

    /// Excel导入物料按钮
    private void ImportMatClick(object sender, EventArgs e)
    {
    var res = ExcelFixedTemplate.ImportMaterial();
    if (res.Success && res.ImportMaterial.Count > 0)
    LocalSqlDb.Insert(res.ImportMaterial);
    MessageBox.Show(res.Msg);
    LoadAllTableData();
    }

    #region 窗体控件定义(VS设计器自动生成)
    private DataGridView dgvMaterial, dgvBom, dgvWork;
    private ComboBox cboSingleAxis, cboLinkGroup;
    private CheckedListBox clbCombine;
    private TextBox txtLog, txtBomNo, txtPlanQty;
    private Button btnCreateWork, btnPauseWork, btnArchiveWork;
    private Panel panelChart;
    private void InitializeComponent()
    {
    this.ClientSize = new Size(1300, 860);
    // 省略VS自动布局代码
    }
    #endregion
    }
    }

    六、Program 程序入口

    csharp

    运行

    using System;
    using System.Windows.Forms;
    using _3C_MES_ERP.Forms;

    namespace _3C_MES_ERP
    {
    static class Program
    {
    [STAThread]
    static void Main()
    {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainCooperForm());
    }
    }
    }

    五、项目完整介绍

    1. 项目解决核心痛点

  • 业务代码碎片化:原调度、Excel、绘图、数据库逻辑全部散落在窗体按钮事件内,维护困难;本方案按领域拆独立组件,单一职责,可单独复用。
  • 参数硬编码:所有数据库、线程、图表、ERP、Excel 路径统一放入App.config,改配置无需修改代码。
  • 多线程混乱:统一封装多线程调度组件,自动汇总所有并行线程执行结果并持久化,消除零散 Task/Thread 代码。
  • 业务耦合严重:物料 / BOM / 组轴 / 作业分层隔离,通过事件总线解耦模块通信,无跨层硬依赖。
  • 2. 核心业务能力清单

  • 本地文件 SQLite 数据库 免数据库服务,程序启动自动建表、生成 15~17 组 3C 电子物料 / BOM 测试数据,SqlSugar 完整封装 CRUD。
  • 组轴 + 动力设备协同执行引擎 模拟 3C 产线 SMT / 焊接 / 组装 / 检测四大组轴,工序执行自动变更设备负载,支持单轴暂停、整组紧急停机。
  • 三模式多线程调度 ①单轴独立调度;②多轴并行组合调度;③流水线联控调度(一故障全流水线停止);所有线程执行文本自动组装存入调度日志。
  • ERP 物料 BOM 完整协同流程 物料档案→BOM 结构→生产作业单,作业生命周期严格贴合金蝶 ERP 单据:创建→调度→运行→暂停→完成→归档,禁止跨状态非法操作。
  • 标准化 Excel 固定模板 两套不可修改标准模板,支持下载空白模板、导入校验组别 15~17、批量导出物料 / BOM 数据。
  • 原生 GDI 图表控件 不依赖第三方图表库,自绘产量柱状图 + 设备负载仪表盘,绘图参数全部从 App.config 读取,可嵌入任意窗体。
  • 全局事件总线 日志、界面刷新、调度通知、作业状态变更统一发布订阅,各组件无直接依赖。
  • WinForm 轻量化界面 界面只做展示与按钮交互,无任何复杂业务逻辑,所有操作调用底层组件。
  • 3. 架构优势

  • 组件可移植:Db/Excel/GDI/ 调度引擎均可单独提取到其他 WinForm 项目复用;
  • 易扩展:新增工序、报表、ERP 接口仅扩展对应组件,不改动其他模块;
  • 环境快速切换:修改 App.config 参数即可切换数据库、ERP 地址、线程并发数;
  • 可维护:业务按领域拆分,定位 BUG 只需进入对应组件,不用翻遍窗体零散代码;
  • 工业场景适配:完全贴合 3C 数码组装 MES+ERP 协同生产业务流程。
  • 赞(0)
    未经允许不得转载:171主机测评 » 组件化 3C 电子 MES/ERP 协同项目全套方案
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址