欢迎光临
我们一直在努力

组轴式执行引擎 ✅ 多线程调度组装组件

整体设计方案

核心设计准则

  • 实体分层清晰、简洁
    • 分层:DB实体(仅数据持久层)→业务DTO(组件之间传输)
    • 数据库实体仅在本地数据库组件内部使用,向外输出统一业务 DTO,避免全局到处引用数据库实体;杜绝大而全的实体,字段按需精简。
  • 业务目标(3C 电子组装产线:SMT 贴片 AX01、回流焊 AX02、整机组装 AX03、功能检测 AX04)
    • 组轴执行引擎(组支轴模式)
    • 多线程调度 + 多任务执行结果统一汇总组装
    • 固定 Excel 模板导入导出(物料、多级 BOM,3C 物料组别 15/16/17 校验)
    • 原生 GDI + 自定义图表控件(产量柱状图、设备负载仪表盘报表)
    • ERP 物料 + BOM 主子分层树形协同场景
    • SQLite 本地文件数据库可视化管理组件
  • 架构规范
    • 所有业务封装独立组件,消除窗体零散 Click 业务代码;
    • WinForm 窗体只负责 UI 交互展示,不编写业务逻辑;
    • 组件通过全局事件总线解耦通信,减少硬依赖;
  • 技术栈:.NET Framework4.8 WinForm、SqlSugar、NPOI、原生 GDI+、Task 异步并发
  • 项目目录结构

    plaintext

    3C_MES_WinStationClient
    ├─ App.config
    ├─ Program.cs
    ├─ DataTransfer # 【业务DTO,组件通用传输对象,轻量化】
    │ AxisDto.cs
    │ MaterialDto.cs
    │ BomNodeDto.cs
    │ ScheduleResultDto.cs
    │ ChartStatDto.cs
    ├─ Components
    │ ├─ LocalDbComponent # 本地SQLite文件数据库模块
    │ │ DatabaseEntity/ # 数据库实体,仅限本组件内部访问
    │ │ AxisDbEntity.cs
    │ │ MaterialDbEntity.cs
    │ │ BomDbEntity.cs
    │ │ ScheduleLogDbEntity.cs
    │ │ SqliteDataProvider.cs # 数据提供层,DbEntity→DTO转换
    │ │ DbManageForm.cs # 数据库可视化管理窗体
    │ ├─ AxisEngineComponent # 组轴执行引擎
    │ │ AxisExecuteEngine.cs
    │ ├─ MultiScheduleComponent # 多线程调度+结果组装
    │ │ ScheduleTaskScheduler.cs
    │ ├─ BomCooperComponent # ERP物料BOM树形协同
    │ │ BomTreeBuilder.cs
    │ ├─ ExcelTemplateComponent # 固定Excel模板导入导出
    │ │ FixedExcelHelper.cs
    │ ├─ GdiChartComponent # GDI自定义绘图控件
    │ │ BaseGdiRender.cs
    │ │ OutputBarChart.cs
    │ │ DeviceLoadGauge.cs
    │ ├─ ApiClientComponent # 对接 IFactoryMom5.Api-dev
    │ ├─ IotMqttComponent # 对接iot-master物联网采集
    │ └─ GlobalEventBus
    │ GlobalEventBus.cs
    ├─ Forms # UI窗体,只处理界面交互
    │ MainFrm
    │ AxisScheduleFrm
    │ BomManageFrm
    │ StatReportFrm
    ├─ Utils
    └─ Resources 内置Excel标准模板

    一、通用传输 DTO(DataTransfer 文件夹)

    DataTransfer/AxisDto.cs

    csharp

    运行

    namespace DataTransfer
    {
    /// <summary>产线组轴 业务传输DTO</summary>
    public class AxisDto
    {
    public string AxisCode { get; set; }
    public string AxisName { get; set; }
    public string LinkedGroup { get; set; }
    public string RunState { get; set; }
    public string BindProcess { get; set; }
    }
    }

    DataTransfer/MaterialDto.cs

    csharp

    运行

    namespace DataTransfer
    {
    /// <summary>物料 DTO,ERP物料同步、Excel导入共用</summary>
    public class MaterialDto
    {
    public string MatCode { get; set; }
    public string MatName { get; set; }
    public string Spec { get; set; }
    public string Unit { get; set; }
    public decimal StockQty { get; set; }
    public int GroupNo { get; set; } //15原材料/16半成品/17成品
    public string MatType { get; set; }
    }
    }

    DataTransfer/BomNodeDto.cs

    csharp

    运行

    using System.Collections.Generic;
    namespace DataTransfer
    {
    /// <summary>BOM树形节点DTO,主子分层结构</summary>
    public class BomNodeDto
    {
    public long Id { get; set; }
    public string BomNo { get; set; }
    public string MaterialCode { get; set; }
    public long ParentNodeId { get; set; }
    public int BomLevel { get; set; }
    public decimal UseQty { get; set; }
    public string BindAxisCode { get; set; }
    public List<BomNodeDto> ChildNodes { get; set; } = new List<BomNodeDto>();
    }
    }

    DataTransfer/ScheduleResultDto.cs

    csharp

    运行

    using System;
    namespace DataTransfer
    {
    /// <summary>多线程调度完成后组装汇总结果DTO</summary>
    public class ScheduleResultDto
    {
    public string ScheduleMode { get; set; }
    public string TargetInfo { get; set; }
    public string Operator { get; set; }
    public string FullMergeLog { get; set; }
    public DateTime OperTime { get; set; }
    }
    }

    DataTransfer/ChartStatDto.cs

    csharp

    运行

    namespace DataTransfer
    {
    /// <summary>GDI图表统计数据DTO</summary>
    public class ChartStatDto
    {
    public string WorkNo { get; set; }
    public decimal PlanQty { get; set; }
    public decimal ActualQty { get; set; }
    }
    }

    二、组件 1:LocalDbComponent 本地 SQLite 数据库模块

    Components/LocalDbComponent/DatabaseEntity/AxisDbEntity.cs

    csharp

    运行

    namespace Components.LocalDbComponent.DatabaseEntity
    {
    /// <summary>数据库实体【仅本组件内部使用】</summary>
    internal class AxisDbEntity
    {
    public long Id { get; set; }
    public string AxisCode { get; set; }
    public string AxisName { get; set; }
    public string LinkedGroup { get; set; }
    public string RunState { get; set; }
    public string BindProcess { get; set; }
    }
    }

    Components/LocalDbComponent/DatabaseEntity/MaterialDbEntity.cs

    csharp

    运行

    using System;
    namespace Components.LocalDbComponent.DatabaseEntity
    {
    internal class MaterialDbEntity
    {
    public long Id { get; set; }
    public string MatCode { get; set; }
    public string MatName { get; set; }
    public string Spec { get; set; }
    public string Unit { get; set; }
    public decimal StockQty { get; set; }
    public int GroupNo { get; set; }
    public string MatType { get; set; }
    public DateTime CreateTime { get; set; }
    }
    }

    Components/LocalDbComponent/DatabaseEntity/BomDbEntity.cs

    csharp

    运行

    namespace Components.LocalDbComponent.DatabaseEntity
    {
    internal class BomDbEntity
    {
    public long Id { get; set; }
    public string BomNo { get; set; }
    public string MaterialCode { get; set; }
    public long ParentNodeId { get; set; }
    public int BomLevel { get; set; }
    public decimal UseQty { get; set; }
    public string BindAxisCode { get; set; }
    public int SortIndex { get; set; }
    }
    }

    Components/LocalDbComponent/DatabaseEntity/ScheduleLogDbEntity.cs

    csharp

    运行

    using System;
    namespace Components.LocalDbComponent.DatabaseEntity
    {
    internal class ScheduleLogDbEntity
    {
    public long Id { get; set; }
    public string ScheduleMode { get; set; }
    public string TargetInfo { get; set; }
    public string Operator { get; set; }
    public string FullMergeLog { get; set; }
    public DateTime OperTime { get; set; }
    }
    }

    Components/LocalDbComponent/SqliteDataProvider.cs

    核心:内部 DbEntity ↔ 对外 DTO 转换,上层业务看不到数据库实体

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.IO;
    using SqlSugar;
    using Components.LocalDbComponent.DatabaseEntity;
    using DataTransfer;
    using Components.GlobalEventBus;

    namespace Components.LocalDbComponent
    {
    public static class SqliteDataProvider
    {
    private static SqlSugarClient _db;
    private static readonly string _dbFileName;

    static SqliteDataProvider()
    {
    _dbFileName = ConfigurationManager.AppSettings["SqliteDbFileName"];
    string fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _dbFileName);
    string connStr = ConfigurationManager.ConnectionStrings["SqliteConn"]
    .Replace("{DbPath}", fullPath);

    _db = new SqlSugarClient(new ConnectionConfig
    {
    ConnectionString = connStr,
    DbType = DbType.Sqlite,
    IsAutoCloseConnection = true
    });
    InitDatabaseTables();
    }

    private static void InitDatabaseTables()
    {
    //自动创建数据表(内部实体)
    _db.CodeFirst.InitTables(
    typeof(AxisDbEntity),
    typeof(MaterialDbEntity),
    typeof(BomDbEntity),
    typeof(ScheduleLogDbEntity));
    InitBaseAxisData();
    }

    //初始化3C标准4条产线组轴
    private static void InitBaseAxisData()
    {
    var axisList = new List<AxisDbEntity>()
    {
    new AxisDbEntity{AxisCode="AX01",AxisName="SMT贴片",LinkedGroup="LINK01",RunState="Idle",BindProcess="锡膏印刷、元件贴片"},
    new AxisDbEntity{AxisCode="AX02",AxisName="回流焊",LinkedGroup="LINK01",RunState="Idle"},
    new AxisDbEntity{AxisCode="AX03",AxisName="整机组装",LinkedGroup="LINK02",RunState="Idle"},
    new AxisDbEntity{AxisCode="AX04",AxisName="功能检测",LinkedGroup="LINK02",RunState="Idle"}
    };
    _db.Insertable(axisList).ExecuteCommand();
    }

    #region 对外公开接口【返回DTO】
    public static List<AxisDto> QueryAllAxis()
    {
    var dbList = _db.Queryable<AxisDbEntity>().ToList();
    List<AxisDto> result = new List<AxisDto>();
    foreach (var item in dbList)
    {
    result.Add(new AxisDto()
    {
    AxisCode = item.AxisCode,
    AxisName = item.AxisName,
    LinkedGroup = item.LinkedGroup,
    RunState = item.RunState,
    BindProcess = item.BindProcess
    });
    }
    return result;
    }

    public static bool UpdateAxisState(AxisDto dto)
    {
    var entity = _db.Queryable<AxisDbEntity>()
    .Where(x => x.AxisCode == dto.AxisCode).First();
    if (entity == null) return false;
    entity.RunState = dto.RunState;
    return _db.Updateable(entity).ExecuteCommand() > 0;
    }

    public static List<MaterialDto> QueryAllMaterial()
    {
    var dbList = _db.Queryable<MaterialDbEntity>().ToList();
    List<MaterialDto> dtoList = new List<MaterialDto>();
    foreach (var m in dbList)
    {
    dtoList.Add(new MaterialDto()
    {
    MatCode = m.MatCode,
    MatName = m.MatName,
    Spec = m.Spec,
    Unit = m.Unit,
    StockQty = m.StockQty,
    GroupNo = m.GroupNo,
    MatType = m.MatType
    });
    }
    return dtoList;
    }

    //批量物料入库(Excel导入调用)
    public static bool BatchInsertMaterial(List<MaterialDto> dtoList)
    {
    return TransactionExecute(() =>
    {
    List<MaterialDbEntity> dbEntityList = new List<MaterialDbEntity>();
    foreach (var dto in dtoList)
    {
    dbEntityList.Add(new MaterialDbEntity()
    {
    MatCode = dto.MatCode,
    MatName = dto.MatName,
    Spec = dto.Spec,
    Unit = dto.Unit,
    StockQty = dto.StockQty,
    GroupNo = dto.GroupNo,
    MatType = dto.MatType,
    CreateTime = DateTime.Now
    });
    }
    _db.Insertable(dbEntityList).ExecuteCommand();
    });
    }

    //保存调度汇总日志
    public static bool SaveScheduleLog(ScheduleResultDto dto)
    {
    var logEntity = new ScheduleLogDbEntity()
    {
    ScheduleMode = dto.ScheduleMode,
    TargetInfo = dto.TargetInfo,
    Operator = dto.Operator,
    FullMergeLog = dto.FullMergeLog,
    OperTime = dto.OperTime
    };
    return _db.Insertable(logEntity).ExecuteCommand() > 0;
    }
    #endregion

    #region 事务、数据库运维方法(可视化窗体调用)
    public static bool TransactionExecute(Action action)
    {
    try
    {
    _db.Ado.BeginTran();
    action.Invoke();
    _db.Ado.CommitTran();
    return true;
    }
    catch (Exception ex)
    {
    _db.Ado.RollbackTran();
    GlobalEventBus.PublishLog($"事务执行失败:{ex.Message}");
    return false;
    }
    }

    public static bool BackupDatabase(string savePath)
    {
    try
    {
    string sourcePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _dbFileName);
    File.Copy(sourcePath, savePath, true);
    return true;
    }
    catch
    {
    return false;
    }
    }

    public static void ClearScheduleLog()
    {
    _db.Deleteable<ScheduleLogDbEntity>().Where(x => x.Id > 0).ExecuteCommand();
    }

    public static string GetDbFullPath() => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _dbFileName);
    #endregion
    }
    }

    Components/LocalDbComponent/DbManageForm.cs(数据库可视化窗口)

    csharp

    运行

    using System;
    using System.Windows.Forms;

    namespace Components.LocalDbComponent
    {
    public partial class DbManageForm : Form
    {
    public DbManageForm()
    {
    InitializeComponent();
    lblDbPath.Text = $"数据库路径:{SqliteDataProvider.GetDbFullPath()}";
    RefreshStatistics();
    }

    private void RefreshStatistics()
    {
    var matCount = SqliteDataProvider.QueryAllMaterial().Count;
    var axisCount = SqliteDataProvider.QueryAllAxis().Count;
    txtStat.Text = $"物料总数:{matCount}\\r\\n产线组轴数量:{axisCount}";
    }

    private void btnBackup_Click(object sender, EventArgs e)
    {
    SaveFileDialog sfd = new SaveFileDialog();
    sfd.Filter = "*.db";
    sfd.FileName = $"MES数据库备份_{DateTime.Now:yyyyMMdd}.db";
    if (sfd.ShowDialog() == DialogResult.OK)
    {
    bool res = SqliteDataProvider.BackupDatabase(sfd.FileName);
    MessageBox.Show(res ? "备份成功" : "备份失败");
    RefreshStatistics();
    }
    }

    private void btnClearLog_Click(object sender, EventArgs e)
    {
    if (MessageBox.Show("确认清空调度日志?数据不可恢复", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes)
    return;
    SqliteDataProvider.ClearScheduleLog();
    MessageBox.Show("日志已清空");
    RefreshStatistics();
    }
    }
    }

    三、组件 2:AxisEngineComponent 组轴执行引擎

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using Components.LocalDbComponent;
    using Components.GlobalEventBus;
    using DataTransfer;
    using System.Configuration;

    namespace Components.AxisEngineComponent
    {
    public class AxisExecuteEngine
    {
    private readonly int _simDelayMs;
    private readonly Dictionary<string, AxisDto> _axisCache = new Dictionary<string, AxisDto>();

    public AxisExecuteEngine()
    {
    _simDelayMs = int.Parse(ConfigurationManager.AppSettings["AxisSimDelayMs"]);
    LoadAxisCache();
    }

    private void LoadAxisCache()
    {
    var list = SqliteDataProvider.QueryAllAxis();
    _axisCache.Clear();
    foreach (var dto in list)
    {
    _axisCache[dto.AxisCode] = dto;
    }
    }

    /// <summary>单组轴执行加工工序,返回完整执行日志</summary>
    public string RunSingleAxisProcess(string axisCode)
    {
    if (!_axisCache.ContainsKey(axisCode) || _axisCache[axisCode].RunState != "Idle")
    return $"【{axisCode}】设备当前繁忙,无法启动工序";

    AxisDto targetAxis = _axisCache[axisCode];
    targetAxis.RunState = "Running";
    SqliteDataProvider.UpdateAxisState(targetAxis);
    GlobalEventBus.PublishAxisState(axisCode, "开始执行工序");

    List<string> logBuffer = new List<string>();
    logBuffer.Add($"=====组轴{axisCode}【{targetAxis.AxisName}】工序开始=====");
    string[] processes = targetAxis.BindProcess?.Split('、') ?? new[] { "标准加工工序" };

    foreach (var proc in processes)
    {
    Thread.Sleep(_simDelayMs);
    logBuffer.Add($"工序:{proc} 执行完成");
    }

    targetAxis.RunState = "Idle";
    SqliteDataProvider.UpdateAxisState(targetAxis);
    logBuffer.Add($"=====组轴{axisCode}工序全部执行完毕=====\\r\\n");
    string fullLog = string.Join("\\r\\n", logBuffer);
    GlobalEventBus.PublishLog(fullLog);
    return fullLog;
    }

    public void PauseAxis(string axisCode)
    {
    if (_axisCache.TryGetValue(axisCode, out var axis) && axis.RunState == "Running")
    {
    axis.RunState = "Paused";
    SqliteDataProvider.UpdateAxisState(axis);
    GlobalEventBus.PublishLog($"组轴{axisCode}已手动暂停");
    }
    }

    public void EmergencyStopLinkedGroup(string linkGroup)
    {
    var groupAxisList = _axisCache.Values.Where(x => x.LinkedGroup == linkGroup).ToList();
    foreach (var ax in groupAxisList)
    {
    ax.RunState = "Error";
    SqliteDataProvider.UpdateAxisState(ax);
    }
    GlobalEventBus.PublishLog($"联控分组 {linkGroup} 紧急停机");
    }

    public List<string> GetAllAxisCode() => _axisCache.Keys.ToList();
    public List<string> GetAllLinkedGroup() => _axisCache.Values.Select(x => x.LinkedGroup).Distinct().ToList();
    }
    }

    四、组件 3:MultiScheduleComponent 多线程调度 + 结果组装

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Components.AxisEngineComponent;
    using Components.LocalDbComponent;
    using Components.GlobalEventBus;
    using DataTransfer;

    namespace Components.MultiScheduleComponent
    {
    public class ScheduleTaskScheduler
    {
    private readonly AxisExecuteEngine _axisEngine;

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

    /// <summary>单轴异步调度</summary>
    public async Task<ScheduleResultDto> RunSingleTask(string axisCode, string operatorName)
    {
    var task = Task.Run(() => _axisEngine.RunSingleAxisProcess(axisCode));
    await task;
    string logText = task.Result;

    var resultDto = new ScheduleResultDto()
    {
    ScheduleMode = "SingleAxis",
    TargetInfo = axisCode,
    Operator = operatorName,
    FullMergeLog = logText,
    OperTime = DateTime.Now
    };
    SqliteDataProvider.SaveScheduleLog(resultDto);
    GlobalEventBus.PublishScheduleComplete(logText);
    return resultDto;
    }

    /// <summary>多轴并行调度,多线程并发执行,统一汇总所有线程输出日志</summary>
    public async Task<ScheduleResultDto> RunCombineParallelTask(List<string> axisCodes, string operatorName)
    {
    List<Task<string>> taskList = new List<Task<string>>();
    foreach (var ax in axisCodes)
    {
    taskList.Add(Task.Run(() => _axisEngine.RunSingleAxisProcess(ax)));
    }
    await Task.WhenAll(taskList);

    string mergeLog = "【多轴并行调度汇总】\\r\\n"
    + string.Join("\\r\\n—————-\\r\\n", taskList.Select(t => t.Result));

    var resultDto = new ScheduleResultDto()
    {
    ScheduleMode = "CombineParallel",
    TargetInfo = string.Join("、", axisCodes),
    Operator = operatorName,
    FullMergeLog = mergeLog,
    OperTime = DateTime.Now
    };
    SqliteDataProvider.SaveScheduleLog(resultDto);
    GlobalEventBus.PublishScheduleComplete(mergeLog);
    return resultDto;
    }

    /// <summary>流水线顺序联控调度</summary>
    public async Task<ScheduleResultDto> RunLinkedPipelineTask(string linkGroup, string operatorName)
    {
    var axisList = _axisEngine.GetAllAxisCode()
    .Where(c => _axisEngine.GetAllLinkedGroup().Contains(linkGroup)).ToList();
    string totalLog = $"【流水线联控 {linkGroup}】执行日志\\r\\n";

    foreach (var ax in axisList)
    {
    string log = await Task.Run(() => _axisEngine.RunSingleAxisProcess(ax));
    totalLog += log;
    if (log.Contains("过载预警"))
    {
    _axisEngine.EmergencyStopLinkedGroup(linkGroup);
    totalLog += ">>>> 设备异常,流水线强制停机 <<<<\\r\\n";
    break;
    }
    }

    var resultDto = new ScheduleResultDto()
    {
    ScheduleMode = "LinkedPipeline",
    TargetInfo = linkGroup,
    Operator = operatorName,
    FullMergeLog = totalLog,
    OperTime = DateTime.Now
    };
    SqliteDataProvider.SaveScheduleLog(resultDto);
    GlobalEventBus.PublishScheduleComplete(totalLog);
    return resultDto;
    }

    public void StopAllTask()
    {
    foreach (var axCode in _axisEngine.GetAllAxisCode())
    {
    _axisEngine.PauseAxis(axCode);
    }
    }
    }
    }

    五、组件 4:ExcelTemplateComponent 固定 Excel 模板导入导出

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Windows.Forms;
    using NPOI.SS.UserModel;
    using NPOI.XSSF.UserModel;
    using Components.LocalDbComponent;
    using DataTransfer;

    namespace Components.ExcelTemplateComponent
    {
    public static class FixedExcelHelper
    {
    private static readonly List<string> MaterialHeader = new List<string>()
    { "物料编码", "物料名称", "规格型号", "单位", "库存数量", "组别(15/16/17)", "物料类型" };

    public static void DownloadMaterialBlankTemplate()
    {
    SaveFileDialog sfd = new SaveFileDialog
    {
    Filter = "Excel文件(*.xlsx)|*.xlsx",
    FileName = "3C电子物料标准模板.xlsx"
    };
    if (sfd.ShowDialog() != DialogResult.OK) return;
    CreateTemplateFile(MaterialHeader, sfd.FileName);
    MessageBox.Show("空白物料模板下载完成");
    }

    private static void CreateTemplateFile(List<string> header, string path)
    {
    using IWorkbook wb = new XSSFWorkbook();
    ISheet sheet = wb.CreateSheet("标准模板");
    IRow headerRow = sheet.CreateRow(0);
    for (int i = 0; i < header.Count; i++)
    {
    headerRow.CreateCell(i).SetCellValue(header[i]);
    sheet.SetColumnWidth(i, 24 * 256);
    }
    using var fs = new FileStream(path, FileMode.Create);
    wb.Write(fs);
    }

    /// <summary>导入物料模板,强制校验组别15/16/17</summary>
    public static (bool success, string msg, List<MaterialDto> dataList) ImportMaterialExcel()
    {
    List<MaterialDto> resultList = new List<MaterialDto>();
    OpenFileDialog ofd = new OpenFileDialog { Filter = "*.xlsx" };
    if (ofd.ShowDialog() != DialogResult.OK)
    return (false, "取消文件选择", resultList);

    try
    {
    using var fs = new FileStream(ofd.FileName, FileMode.Open);
    IWorkbook wb = new XSSFWorkbook(fs);
    ISheet sheet = wb.GetSheetAt(0);
    for (int r = 1; r <= sheet.LastRowNum; r++)
    {
    IRow row = sheet.GetRow(r);
    if (row == null) continue;

    int groupNo = int.TryParse(GetCellText(row, 5), out int g) ? g : 0;
    if (groupNo < 15 || groupNo > 17)
    return (false, $"第{r + 1}行物料组别必须为15/16/17", resultList);

    var dto = new MaterialDto()
    {
    MatCode = GetCellText(row, 0),
    MatName = GetCellText(row, 1),
    Spec = GetCellText(row, 2),
    Unit = GetCellText(row, 3),
    StockQty = decimal.TryParse(GetCellText(row, 4), out var q) ? q : 0,
    GroupNo = groupNo,
    MatType = GetCellText(row, 6)
    };
    resultList.Add(dto);
    }
    bool insertOk = SqliteDataProvider.BatchInsertMaterial(resultList);
    return (insertOk, $"成功读取{resultList.Count}条物料", resultList);
    }
    catch (Exception ex)
    {
    return (false, $"解析异常:{ex.Message}", resultList);
    }
    }

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

    六、组件 5:GlobalEventBus 全局事件总线(模块解耦)

    csharp

    运行

    using System;

    namespace Components.GlobalEventBus
    {
    public static class GlobalEventBus
    {
    public delegate void LogMessageHandler(string logContent);
    public delegate void AxisStateHandler(string axisCode, string info);
    public delegate void ScheduleCompleteHandler(string fullLog);
    public delegate void UiRefreshHandler();

    public static event LogMessageHandler OnLogOutput;
    public static event AxisStateHandler OnAxisStateChanged;
    public static event ScheduleCompleteHandler OnScheduleFinish;
    public static event UiRefreshHandler OnRequestUiRefresh;

    public static void PublishLog(string text)
    {
    string log = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | {text}";
    OnLogOutput?.Invoke(log);
    }

    public static void PublishAxisState(string axisCode, string msg)
    {
    PublishLog($"【组轴{axisCode}】{msg}");
    OnAxisStateChanged?.Invoke(axisCode, msg);
    }

    public static void PublishScheduleComplete(string fullLog)
    {
    OnScheduleFinish?.Invoke(fullLog);
    }

    public static void PublishUiRefresh()
    {
    OnRequestUiRefresh?.Invoke();
    }
    }
    }

    七、窗体调用示例(窗体仅做 UI 交互,无业务逻辑)

    csharp

    运行

    using System;
    using System.Collections.Generic;
    using System.Windows.Forms;
    using Components.AxisEngineComponent;
    using Components.MultiScheduleComponent;
    using Components.ExcelTemplateComponent;
    using Components.GlobalEventBus;

    namespace Forms
    {
    public partial class AxisScheduleFrm : Form
    {
    private readonly AxisExecuteEngine _axisEngine;
    private readonly ScheduleTaskScheduler _scheduler;

    public AxisScheduleFrm()
    {
    _axisEngine = new AxisExecuteEngine();
    _scheduler = new ScheduleTaskScheduler(_axisEngine);
    InitializeComponent();
    BindEventBus();
    InitUiSource();
    }

    private void BindEventBus()
    {
    GlobalEventBus.OnLogOutput += log => txtLog.AppendText(log + "\\r\\n");
    GlobalEventBus.OnRequestUiRefresh += RefreshData;
    }

    private void InitUiSource()
    {
    var axisCodes = _axisEngine.GetAllAxisCode();
    cboSingleAxis.Items.AddRange(axisCodes.ToArray());
    clbCombineAxis.Items.AddRange(axisCodes.ToArray());
    cboLinkGroup.Items.AddRange(_axisEngine.GetAllLinkedGroup().ToArray());
    }

    private void RefreshData()
    {
    //刷新表格、树形、图表数据源
    }

    private async void btnSingleRun_Click(object sender, EventArgs e)
    {
    if (cboSingleAxis.SelectedItem == null) return;
    await _scheduler.RunSingleTask(cboSingleAxis.Text, "车间操作员");
    }

    private async void btnCombineRun_Click(object sender, EventArgs e)
    {
    List<string> selectAxis = new List<string>();
    for (int i = 0; i < clbCombineAxis.Items.Count; i++)
    {
    if (clbCombineAxis.GetItemChecked(i))
    selectAxis.Add(clbCombineAxis.Items[i].ToString());
    }
    if (selectAxis.Count == 0)
    {
    MessageBox.Show("请勾选至少一条产线组轴");
    return;
    }
    await _scheduler.RunCombineParallelTask(selectAxis, "车间操作员");
    }

    private void btnDownloadTemplate_Click(object sender, EventArgs e)
    {
    FixedExcelHelper.DownloadMaterialBlankTemplate();
    }

    private void btnImportMaterial_Click(object sender, EventArgs e)
    {
    var res = FixedExcelHelper.ImportMaterialExcel();
    MessageBox.Show(res.msg);
    GlobalEventBus.PublishUiRefresh();
    }

    private void btnOpenDbManager_Click(object sender, EventArgs e)
    {
    new Components.LocalDbComponent.DbManageForm().ShowDialog();
    }
    }
    }

    架构优势说明

  • 实体分层清晰简洁
    • 数据库实体 DbEntity 隔离在数据库组件内部,不对外暴露;
    • 上层业务全部使用轻量化 DTO,分层边界明确;避免全局大实体到处引用造成耦合。
  • 业务全部组件化 消除窗体零散业务代码,组轴引擎、多线程调度、Excel 模板、数据库操作各自独立组件。
  • 满足核心业务场景 ✅ 组轴式执行引擎 ✅ 多线程调度 + 任务结果统一组装汇总 ✅ 固定 Excel 模板导入导出、3C 物料组别校验 ✅ 预留扩展:BOM 树形协同组件、GDI 自定义图表控件可直接接入 ✅ SQLite 本地文件数据库可视化管理模块
  • 组件低耦合 使用全局事件总线通信,减少类之间直接实例依赖,方便后续迭代维护。
  • 如果你需要,我可以继续输出:

  • BOM 树形协同 BomTreeBuilder.cs 完整代码
  • GDI 柱状图、仪表盘绘图控件完整代码
  • App.config 配置样板
  • BOM Excel 导入导出补充代码
  • 赞(0)
    未经允许不得转载:171主机测评 » 组轴式执行引擎 ✅ 多线程调度组装组件
    分享到: 更多 (0)

    评论 抢沙发

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