混料生产工单自动下发(仿真模拟器)(1):基础函数封装

最近做一个项目,混料生产需要自动下发当前工单。

整体逻辑是这样的:

混料生产三种类型(A类、B类、C类)的产品,三种产品类型可以对应三个工单

A类型 --工单编号100

B类型 --工单编号200

C类型 --工单编号300

比如依次生产【A--->B--->B--->C】一直这样继续,当出现某一种产品剩余库存为0时,提醒报警。

线体设备(如PLC)在生产时,每次请求一个工单,软件就向线体设备发送一个【当前工单】和【预设下一个工单】,以及软件下发工单操作结果 给 线体设备。

设计思路如下:

第一步:我们需要新建表,工单基本信息表work_order_basic

主要字段有: 工单编号、产品类型,产品批次号,工单总数量,剩余数量

第二步:生产方案工单配方表work_order_plan

表示【某一个轮回周期内,共有几个工单,以及】

主要字段有:方案代码、方案名称、周期工单个数、是否是当前生产方案

第三步:生产方案工单配方详细顺序表work_order_plan_detail

表示【方案相应的工单下发顺序】

主要字段有:方案代码、工单顺序、工单编号、剩余数量、是否是当前生产方案

第四步:当前工单和预设下一工单方案顺序队列表work_order_plan_sequence,

该表只有两行,第一行代表当前下发工单、第二行代表预设下一个工单

我们使用Sqlite本地数据库来处理工单下发。

新建windows窗体应用程序WorkOrderDemo,选择.net 4.5,添加对类库System.Data.SQLite.dll的引用,用于操作sqlite数据库。

一、编写操作sqlite数据库SqliteAssist.cs

类SqliteAssist源程序如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SQLite;
using System.Windows.Forms;
using System.IO;

namespace WorkOrderDemo
{
    /// <summary>
    /// 对sqlite数据库进行操作
    /// </summary>
    public class SqliteAssist
    {
        /// <summary>
        /// sqlite连接
        /// </summary>
        public static SQLiteConnection connection = null;

        /// <summary>
        /// 初始化数据库,如果workOrder.db数据库文件不存在,就创建之,同时创建数据库连接
        /// 自动创建工单相关的四张表
        /// </summary>
        public static void IniDataTable()
        {
            SQLiteConnection connectionIni = new SQLiteConnection();
            string databaseFileName = AppDomain.CurrentDomain.BaseDirectory + "workOrder.db";
            connectionIni.ConnectionString = "Data Source=" + databaseFileName + ";Pooling=true;FailIfMissing=false;Journal Mode=WAL";
            if (!File.Exists(databaseFileName))
            {
                SQLiteConnection.CreateFile(databaseFileName);
                string sqlCreateTable = @"
CREATE TABLE IF NOT EXISTS work_order_basic  --工单基本信息表
(CoreId INTEGER primary key AUTOINCREMENT, --编号
WorkOrderCode VARCHAR(64),--工单编号
WorkOrderName VARCHAR(64),--工单名称
WorkOrderCategory VARCHAR(64),--工单类型
WorkOrderPn VARCHAR(64),--工单批次号
WorkOrderTotal INTEGER,--工单总数
RemainderCount INTEGER,--剩余数量
ProcessEndTime VARCHAR(64)); --操作时间

CREATE TABLE IF NOT EXISTS work_order_plan  --工单生产方案汇总表
(CoreId INTEGER primary key AUTOINCREMENT, --编号
PlanCode VARCHAR(64),--生产方案代码
PlanName VARCHAR(64),--生产方案名称
CycleCount INTEGER,--周期工单个数
Enabled INTEGER,--是否启用
ProcessEndTime VARCHAR(64)); --操作时间

CREATE TABLE IF NOT EXISTS work_order_plan_detail --工单生产方案详细表
(CoreId INTEGER primary key AUTOINCREMENT, --编号
PlanCode VARCHAR(64),--生产方案代码
PlanName VARCHAR(64),--生产方案名称
PlanSequence INTEGER,--工单方案顺序号
OrderCode VARCHAR(64),--工单编号
OrderName VARCHAR(64),--工单名称
WorkOrderCategory VARCHAR(64),--工单类型
WorkOrderPn VARCHAR(64),--工单批次号
OrderTotal INTEGER,--工单总数
RemainderCount INTEGER,--剩余数量
Enabled INTEGER,--是否启用
ProcessEndTime VARCHAR(64)); --操作时间

CREATE TABLE IF NOT EXISTS work_order_plan_sequence --工单生产方案当前队列顺序,有且只有两行记录,当前工单 和 预设工单
(CoreId INTEGER primary key AUTOINCREMENT, --编号
PlanCode VARCHAR(64),--生产方案代码
PlanName VARCHAR(64),--生产方案名称
PlanSequence INTEGER,--工单方案顺序号
OrderCode VARCHAR(64),--工单编号
OrderName VARCHAR(64),--工单名称
WorkOrderCategory VARCHAR(64),--工单类型
WorkOrderPn VARCHAR(64),--工单批次号
OrderTotal INTEGER,--工单总数
RemainderCount INTEGER,--剩余数量
PresetFlag INTEGER,--是否预设工单
ProcessEndTime VARCHAR(64)); --操作时间
";
                
                SQLiteCommand cmd = new SQLiteCommand(sqlCreateTable, connectionIni);
                try
                {
                    if (connectionIni.State != ConnectionState.Open)
                    {
                        connectionIni.Open();
                    }
                    cmd.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "创建表时出现异常");
                }
                finally
                {
                    connectionIni.Close();
                }                
            }

            //为连接赋值
            connection = connectionIni;
        }

        /// <summary>
        /// 生成sqlite命令
        /// </summary>
        /// <param name="sql">sql语句或者存储过程</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns></returns>
        public static SQLiteCommand GenerateCommand(string sql, Dictionary<string, object> dict)
        {
            SQLiteCommand cmd = new SQLiteCommand(sql, connection);
            //cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Clear();
            if (dict != null)
            {
                foreach (string parameterName in dict.Keys)
                {
                    cmd.Parameters.AddWithValue(parameterName, dict[parameterName]);
                }
            }
            return cmd;
        }

        /// <summary>
        /// 更新数据,执行增(insert into)、删(delete)、改(update)、创建表(create table)等操作
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns>返回:受影响的行数</returns>
        public static int ExecuteCommand(string sql, Dictionary<string, object> dict)
        {
            int affectCount = -1;
            SQLiteCommand cmd = GenerateCommand(sql, dict);
            try
            {
                if (connection.State != ConnectionState.Open)
                {
                    connection.Open();
                }
                affectCount = cmd.ExecuteNonQuery();                
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "更改数据时出现异常");
            }
            finally
            {
                connection.Close();
            }
            return affectCount;
        }

        /// <summary>
        /// 事务处理
        /// </summary>
        /// <param name="sqlCollection">sql语句集合</param>
        /// <param name="dictCollection">每一条对应sql中的参数以及对应的值,没有参数时为null</param>
        /// <returns></returns>
        public static bool ProcessTransaction(List<string> sqlCollection, List<Dictionary<string, object>> dictCollection)
        {
            bool processResult = false;
            if (sqlCollection == null || dictCollection == null || sqlCollection.Count != dictCollection.Count)
            {
                throw new Exception("没有sql指令 或者 参数个数不匹配");
            }
            using (SQLiteTransaction transaction = connection.BeginTransaction())
            {
                try
                {
                    if (connection.State != ConnectionState.Open)
                    {
                        connection.Open();
                    }
                    for (int i = 0; i < sqlCollection.Count; i++)
                    {
                        SQLiteCommand cmd = GenerateCommand(sqlCollection[i], dictCollection[i]);
                        cmd.ExecuteNonQuery();
                    }
                    //提交事务
                    transaction.Commit();
                    processResult = true;
                }
                catch (Exception ex)
                {
                    //回滚事务
                    transaction.Rollback();
                    MessageBox.Show(ex.Message, "事务处理时出现异常");
                    processResult = false;
                }
                finally
                {
                    connection.Close();
                }
            }
            return processResult;
        }

        /// <summary>
        /// 获取查询的结果,存入一个数据集中,该函数多用于返回多个数据表的查询
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns></returns>
        public static DataSet GetDataSet(string sql, Dictionary<string, object> dict)
        {
            DataSet ds = new DataSet("MyDataSet");
            SQLiteDataAdapter adapter = new SQLiteDataAdapter();
            adapter.SelectCommand = GenerateCommand(sql, dict);
            try
            {
                adapter.Fill(ds);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "查询数据时出现异常");
            }
            return ds;
        }

        /// <summary>
        /// 获取查询的结果,存入一个数据表中
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns></returns>
        public static DataTable GetDataTable(string sql, Dictionary<string, object> dict)
        {
            DataTable dt = new DataTable("MyDataTable");
            SQLiteDataAdapter adapter = new SQLiteDataAdapter();
            adapter.SelectCommand = GenerateCommand(sql, dict);
            try
            {
                adapter.Fill(dt);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "查询数据时出现异常");
            }
            return dt;
        }

        /// <summary>
        /// 获取数据表某一行数据,多用于获取数据库中某一个的ID的数据行
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns></returns>
        public static DataRow GetDataRow(string sql, Dictionary<string, object> dict)
        {
            DataTable dt = GetDataTable(sql, dict);
            if (dt != null && dt.Rows.Count > 0)
            {
                return dt.Rows[0];
            }
            return null;
        }

        /// <summary>
        /// 执行查询,并返回查询所返回的结果集中第一行的第一列。所有其他的列和行将被忽略
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns></returns>        
        public static object GetScalar(string sql, Dictionary<string, object> dict)
        {
            object result = null;
            SQLiteCommand cmd = GenerateCommand(sql, dict);
            try
            {
                if (connection.State != ConnectionState.Open)
                {
                    connection.Open();
                }
                result = cmd.ExecuteScalar();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "获取第一行的第一列数据时出现异常");
            }
            finally
            {
                connection.Close();
            }
            return result;
        }

        /// <summary>
        /// 执行查询,并返回查询所返回的结果集中第一行的第一列。返回一个字符串
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns></returns>   
        public static string GetScalarString(string sql, Dictionary<string, object> dict)
        {
            object result = GetScalar(sql, dict);
            if (result == null)
            {
                return string.Empty;
            }
            return result.ToString();
        }

        /// <summary>
        /// 执行查询,并返回查询所返回的结果集中第一行的第一列。返回一个整数
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="dict">sql中的参数以及具体的值</param>
        /// <returns></returns>   
        public static int GetScalarInt(string sql, Dictionary<string, object> dict)
        {
            object result = GetScalar(sql, dict);
            if (result == null)
            {
                return 0;
            }
            return Convert.ToInt32(result);
        }

    }
}

二、相关数据表映射的类文件,可以通过代码生成器自动生成

三个类依次为WorkOrderBasic,WorkOrderDemo,WorkOrderPlanDetail

1).类WorkOrderBasic的源程序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WorkOrderDemo
{
    /// <summary>
    /// 工单基本信息表
    /// </summary>
    public class WorkOrderBasic
    {
        /// <summary>
        /// 工单自增编号
        /// </summary>
        public int CoreId { get; set; }
        /// <summary>
        /// 工单编号
        /// </summary>
        public string WorkOrderCode { get; set; }
        /// <summary>
        /// 工单名称
        /// </summary>
        public string WorkOrderName { get; set; }
        /// <summary>
        /// 工单类型
        /// </summary>
        public string WorkOrderCategory { get; set; }
        /// <summary>
        /// 工单批次号
        /// </summary>
        public string WorkOrderPn { get; set; }
        /// <summary>
        /// 工单总数
        /// </summary>
        public int WorkOrderTotal { get; set; }
        /// <summary>
        /// 剩余数量
        /// </summary>
        public int RemainderCount { get; set; }
        /// <summary>
        /// 处理结束时间
        /// </summary>
        public DateTime ProcessEndTime { get; set; }
    }
}

2).类WorkOrderDemo的源程序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WorkOrderDemo
{
    /// <summary>
    /// 产品生产方案【工单方案】主表 需要和方案详细表WorkOrderPlanDetail联合起来使用:
    /// 关键的CycleCount【周期工单个数】,以几个工单作为一个大的整体循环Cycle
    /// 比如 工单方案是 A-A-B-B,则CycleCount为4,同时为WorkOrderPlanDetail增加4行记录
    /// 工单方案主表WorkOrderPlan只有一行有效记录【Enabled=1】作为正在使用的方案,其他老的方案将被作废
    /// </summary>
    public class WorkOrderPlan
    {
        /// <summary>
        /// 自增编号
        /// </summary>
        public int CoreId { get; set; }
        /// <summary>
        /// 生产方案代码 如A-A-B-B
        /// </summary>
        public string PlanCode { get; set; }
        /// <summary>
        /// 生产方案名称
        /// </summary>
        public string PlanName { get; set; }
        /// <summary>
        /// 一个周期内工单个数
        /// </summary>
        public int CycleCount { get; set; }
        /// <summary>
        /// 是否启用 1为启用
        /// </summary>
        public int Enabled { get; set; }
        /// <summary>
        /// 处理结束时间
        /// </summary>
        public DateTime ProcessEndTime { get; set; }
    }
}

3).类WorkOrderPlanDetail的源程序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WorkOrderDemo
{
    /// <summary>
    /// 产品生产方案【工单方案】详细表,比如生产计划是A-A-B-B,
    /// 那么将有四行工单记录,Sequence以此为1,2,3,4
    /// </summary>
    public class WorkOrderPlanDetail
    {
        /// <summary>
        /// 自增编号
        /// </summary>
        public int CoreId { get; set; }
        /// <summary>
        /// 生产方案代码 如A-A-B-B
        /// </summary>
        public string PlanCode { get; set; }
        /// <summary>
        /// 生产方案名称
        /// </summary>
        public string PlanName { get; set; }
        /// <summary>
        /// 工单方案顺序号
        /// </summary>
        public int PlanSequence { get; set; }
        /// <summary>
        /// 工单编号
        /// </summary>
        public string OrderCode { get; set; }
        /// <summary>
        /// 工单名称
        /// </summary>
        public string OrderName { get; set; }
        /// <summary>
        /// 工单类型
        /// </summary>
        public string WorkOrderCategory { get; set; }
        /// <summary>
        /// 工单批次号
        /// </summary>
        public string WorkOrderPn { get; set; }
        /// <summary>
        /// 工单总数
        /// </summary>
        public int OrderTotal { get; set; }
        /// <summary>
        /// 剩余数量
        /// </summary>
        public int RemainderCount { get; set; }
        /// <summary>
        /// 是否启用 1为启用
        /// </summary>
        public int Enabled { get; set; }
        /// <summary>
        /// 处理结束时间
        /// </summary>
        public DateTime ProcessEndTime { get; set; }
    }
}

三、关键的工单处理、下发工单逻辑与流程处理类WorkOrderUtil

核心自动下发工单函数:

bool GetNextAndPresetWorkOrder(out string errMsg, out DataTable dtSequenceAndPreset)

WorkOrderUtil.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WorkOrderDemo
{
    /// <summary>
    /// 工单基本信息 以及 工单生产计划操作
    /// </summary>
    public class WorkOrderUtil
    {
        /// <summary>
        /// 获取所有工单基本信息
        /// </summary>
        /// <returns></returns>
        public static DataTable GetAllWorkOrderBasic() 
        {
            return SqliteAssist.GetDataTable("select * from work_order_basic", null);
        }

        /// <summary>
        /// 获取某一个工单基本信息
        /// </summary>
        /// <param name="workOrderCode"></param>
        /// <returns></returns>
        public static DataTable GetWorkOrderBasic(string workOrderCode)
        {
            return SqliteAssist.GetDataTable("select * from work_order_basic  where WorkOrderCode=@WorkOrderCode", new Dictionary<string, object>() { { "WorkOrderCode", workOrderCode } });
        }

        /// <summary>
        /// 保存一个工单基本信息
        /// </summary>
        /// <param name="workOrderBasic"></param>
        /// <returns></returns>
        public static int SaveWorkOrderBasic(WorkOrderBasic workOrderBasic) 
        {
            object objResult = SqliteAssist.GetScalar("select 1 from work_order_basic where WorkOrderCode=@WorkOrderCode", new Dictionary<string, object>() { { "WorkOrderCode", workOrderBasic.WorkOrderCode } });
            string sql = @"insert into work_order_basic (WorkOrderCode,WorkOrderName,WorkOrderCategory,WorkOrderPn,WorkOrderTotal,RemainderCount,ProcessEndTime) 
values (@WorkOrderCode,@WorkOrderName,@WorkOrderCategory,@WorkOrderPn,@WorkOrderTotal,@RemainderCount,@ProcessEndTime)";
            if (Convert.ToString(objResult) == "1") 
            {
                //工单号存在就修改
                sql = @"update work_order_basic set WorkOrderName=@WorkOrderName,WorkOrderCategory=@WorkOrderCategory,WorkOrderPn=@WorkOrderPn,
WorkOrderTotal=@WorkOrderTotal,RemainderCount=@RemainderCount where WorkOrderCode=@WorkOrderCode;
update work_order_plan_detail set RemainderCount=@RemainderCount where Enabled=1 and OrderCode=@WorkOrderCode";
            }           
            Dictionary<string, object> dict = new Dictionary<string, object>();
            dict.Add("WorkOrderCode", workOrderBasic.WorkOrderCode);
            dict.Add("WorkOrderName", workOrderBasic.WorkOrderName);
            dict.Add("WorkOrderCategory", workOrderBasic.WorkOrderCategory);
            dict.Add("WorkOrderPn", workOrderBasic.WorkOrderPn);
            dict.Add("ProcessEndTime", workOrderBasic.ProcessEndTime);
            dict.Add("WorkOrderTotal", workOrderBasic.WorkOrderTotal);
            dict.Add("RemainderCount", workOrderBasic.RemainderCount);
            int result = SqliteAssist.ExecuteCommand(sql, dict);
            return result;
        }

        /// <summary>
        /// 删除工单基本信息
        /// 如果工单正在使用,则无法删除
        /// </summary>
        /// <returns></returns>
        public static int DeleteWorkOrderBasic(string workOrderCode) 
        {
            object objResult = SqliteAssist.GetScalar("select 1 from work_order_plan_detail where OrderCode=@WorkOrderCode and Enabled='1'", 
                new Dictionary<string, object>() { { "WorkOrderCode", workOrderCode } });
            if (Convert.ToString(objResult) == "1")
            {
                //存在正在使用的工单计划,无法删除
                return -1;
            }
            return SqliteAssist.ExecuteCommand(@"delete from work_order_basic where WorkOrderCode=@WorkOrderCode;
delete from work_order_plan_detail where OrderCode=@WorkOrderCode", new Dictionary<string, object>() { { "WorkOrderCode", workOrderCode } });
        }

        /// <summary>
        /// 启用一个工单方案:【设置为当前生产方案】
        /// 将所有的工单方案作废掉,新增一个新的方案作为当前生产方案
        /// 同时清除工单序号表work_order_plan_sequence
        /// </summary>
        /// <param name="workOrderPlan"></param>
        /// <returns></returns>
        public static int EnableOrderPlan(WorkOrderPlan workOrderPlan, List<WorkOrderPlanDetail> workOrderPlanDetails) 
        {
            string sql = @"update work_order_plan set Enabled=0;
update work_order_plan_detail set Enabled=0;
delete from work_order_plan_sequence;
insert into work_order_plan (PlanCode,PlanName,CycleCount,ProcessEndTime,Enabled) 
values (@PlanCode,@PlanName,@CycleCount,@ProcessEndTime,'1');";
            Dictionary<string, object> dict = new Dictionary<string, object>();
            dict.Add("PlanCode", workOrderPlan.PlanCode);
            dict.Add("PlanName", workOrderPlan.PlanName);
            dict.Add("CycleCount", workOrderPlan.CycleCount);
            dict.Add("ProcessEndTime", workOrderPlan.ProcessEndTime);
            for (int i = 0; i < workOrderPlanDetails.Count; i++)
            {
                sql += $@"insert into work_order_plan_detail (PlanCode,PlanName,PlanSequence,OrderCode,OrderName,WorkOrderCategory,WorkOrderPn,OrderTotal,RemainderCount,ProcessEndTime,Enabled) 
values(@PlanCode,@PlanName,@PlanSequence{i + 1},@OrderCode{i + 1},@OrderName{i + 1},@WorkOrderCategory{i + 1},@WorkOrderPn{i + 1},@OrderTotal{i + 1},@RemainderCount{i + 1},@ProcessEndTime,'1');";
                dict.Add($"PlanSequence{i + 1}", workOrderPlanDetails[i].PlanSequence);
                dict.Add($"OrderCode{i + 1}", workOrderPlanDetails[i].OrderCode);
                dict.Add($"OrderName{i + 1}", workOrderPlanDetails[i].OrderName);
                dict.Add($"WorkOrderCategory{i + 1}", workOrderPlanDetails[i].WorkOrderCategory);
                dict.Add($"WorkOrderPn{i + 1}", workOrderPlanDetails[i].WorkOrderPn);
                dict.Add($"OrderTotal{i + 1}", workOrderPlanDetails[i].OrderTotal);
                dict.Add($"RemainderCount{i + 1}", workOrderPlanDetails[i].RemainderCount);
            }
            return SqliteAssist.ExecuteCommand(sql, dict);
        }

        /// <summary>
        /// 更新工单剩余数量:
        /// 需同时更新两张表
        /// </summary>
        /// <param name="workOrderCode"></param>
        /// <param name="remainderCount"></param>
        /// <returns></returns>
        public static int UpdateWorkOrderRemainder(string workOrderCode, int remainderCount) 
        {
                return SqliteAssist.ExecuteCommand(@"update work_order_basic set RemainderCount=@RemainderCount where WorkOrderCode=@OrderCode;
update work_order_plan_detail set RemainderCount=@RemainderCount where Enabled=1 and OrderCode=@OrderCode",
                        new Dictionary<string, object> { { "RemainderCount", remainderCount }, { "OrderCode", workOrderCode } });
        }

        /// <summary>
        /// 读取当前工单方案信息
        /// </summary>
        /// <returns></returns>
        public static List<WorkOrderPlanDetail> GetCurrentOrderPlan()
        {
            List<WorkOrderPlanDetail> list = new List<WorkOrderPlanDetail>();
            DataTable dt = SqliteAssist.GetDataTable("select * from work_order_plan_detail where Enabled=1 order by PlanSequence", null);
            if (dt == null || dt.Rows.Count == 0) 
            {
                return list;
            }
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                WorkOrderPlanDetail workOrderPlanDetail = new WorkOrderPlanDetail();
                workOrderPlanDetail.CoreId = Convert.ToInt32(dt.Rows[i]["CoreId"]);
                workOrderPlanDetail.PlanCode = dt.Rows[i]["PlanCode"].ToString();
                workOrderPlanDetail.PlanName = dt.Rows[i]["PlanName"].ToString();
                workOrderPlanDetail.PlanSequence = Convert.ToInt32(dt.Rows[i]["PlanSequence"]);
                workOrderPlanDetail.OrderCode = dt.Rows[i]["OrderCode"].ToString();
                workOrderPlanDetail.OrderName = dt.Rows[i]["OrderName"].ToString();
                workOrderPlanDetail.WorkOrderCategory = dt.Rows[i]["WorkOrderCategory"].ToString();
                workOrderPlanDetail.WorkOrderPn = dt.Rows[i]["WorkOrderPn"].ToString();
                workOrderPlanDetail.OrderTotal = Convert.ToInt32(dt.Rows[i]["OrderTotal"]);
                workOrderPlanDetail.RemainderCount = Convert.ToInt32(dt.Rows[i]["RemainderCount"]);
                workOrderPlanDetail.ProcessEndTime = Convert.ToDateTime(dt.Rows[i]["ProcessEndTime"]);
                workOrderPlanDetail.Enabled = Convert.ToInt32(dt.Rows[i]["Enabled"]);
                list.Add(workOrderPlanDetail);
            }
            return list;
        }

        /// <summary>
        /// 查看历史生产工单方案
        /// 【也就是查询无效的、已作废的工单生产方案】
        /// </summary>
        /// <returns></returns>
        public static DataTable GetHistoryOrderPlan()
        {
            return SqliteAssist.GetDataTable("select * from work_order_plan_detail where Enabled=0", null);
        }

        /// <summary>
        /// 一键清除历史工单方案
        /// </summary>
        /// <returns></returns>
        public static int ClearHistoryOrderPlan() 
        {
            return SqliteAssist.ExecuteCommand("delete from work_order_plan where Enabled=0;delete from work_order_plan_detail where Enabled=0", null);
        }

        /// <summary>
        /// 获取指定序号的工单方案信息
        /// 没有找到,则返回null
        /// </summary>
        /// <param name="planSequence"></param>
        /// <returns></returns>
        public static DataRow GetPlanDetailBySequence(int planSequence)
        {
            DataTable dtPlanDetail = SqliteAssist.GetDataTable("select * from work_order_plan_detail where Enabled=1 and PlanSequence=@PlanSequence",
                new Dictionary<string, object>() { { "PlanSequence", planSequence } });
            if (dtPlanDetail == null || dtPlanDetail.Rows.Count == 0)
            {
                return null;
            }
            return dtPlanDetail.Rows[0];
        }

        /// <summary>
        /// 添加一条新的工单序列号信息 PresetFlag=0
        /// </summary>
        /// <param name="drPlanDetail"></param>
        /// <returns></returns>
        public static int InsertNewOrderSequence(DataRow drPlanDetail, int remainderCount)
        {
            string sql = @"
insert into work_order_plan_sequence (PlanCode,PlanName,PlanSequence,OrderCode,OrderName,WorkOrderCategory,WorkOrderPn,OrderTotal,RemainderCount,ProcessEndTime,PresetFlag) 
 values (@PlanCode,@PlanName,@PlanSequence,@OrderCode,@OrderName,@WorkOrderCategory,@WorkOrderPn,@OrderTotal,@RemainderCount,@ProcessEndTime,'0');";
            Dictionary<string, object> dict = new Dictionary<string, object>();
            dict.Add("PlanCode", drPlanDetail["PlanCode"]);
            dict.Add("PlanName", drPlanDetail["PlanName"]);
            dict.Add("PlanSequence", drPlanDetail["PlanSequence"]);
            dict.Add("OrderCode", drPlanDetail["OrderCode"]);
            dict.Add("OrderName", drPlanDetail["OrderName"]);
            dict.Add("WorkOrderCategory", drPlanDetail["WorkOrderCategory"]);
            dict.Add("WorkOrderPn", drPlanDetail["WorkOrderPn"]);
            dict.Add("OrderTotal", drPlanDetail["OrderTotal"]);
            dict.Add("RemainderCount", remainderCount);
            dict.Add("ProcessEndTime", DateTime.Now);
            return SqliteAssist.ExecuteCommand(sql, dict);
        }

        /// <summary>
        /// 添加一条预设的工单序列号信息 PresetFlag=1
        /// </summary>
        /// <param name="drPlanDetail"></param>
        /// <returns></returns>
        public static int InsertPresetOrderSequence(DataRow drPlanDetail, int remainderCount)
        {
            string sql = @"insert into work_order_plan_sequence (PlanCode,PlanName,PlanSequence,OrderCode,OrderName,WorkOrderCategory,WorkOrderPn,OrderTotal,RemainderCount,ProcessEndTime,PresetFlag) 
 values (@PlanCode,@PlanName,@PlanSequence,@OrderCode,@OrderName,@WorkOrderCategory,@WorkOrderPn,@OrderTotal,@RemainderCount,@ProcessEndTime,'1');";
            Dictionary<string, object> dict = new Dictionary<string, object>();
            dict.Add("PlanCode", drPlanDetail["PlanCode"]);
            dict.Add("PlanName", drPlanDetail["PlanName"]);
            dict.Add("PlanSequence", drPlanDetail["PlanSequence"]);
            dict.Add("OrderCode", drPlanDetail["OrderCode"]);
            dict.Add("OrderName", drPlanDetail["OrderName"]);
            dict.Add("WorkOrderCategory", drPlanDetail["WorkOrderCategory"]);
            dict.Add("WorkOrderPn", drPlanDetail["WorkOrderPn"]);
            dict.Add("OrderTotal", drPlanDetail["OrderTotal"]);
            dict.Add("RemainderCount", remainderCount);
            dict.Add("ProcessEndTime", DateTime.Now);
            return SqliteAssist.ExecuteCommand(sql, dict);
        }

        /// <summary>
        /// 添加一条预设工单记录
        /// </summary>
        /// <param name="cycleCount"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        public static bool AddPresetWorkOrderSequence(int cycleCount, out string errMsg) 
        {
            errMsg = string.Empty;
            //新增 预设下一个工单信息
            //查看新增加 当前工单信息
            DataTable dtCurrent = SqliteAssist.GetDataTable("select * from work_order_plan_sequence  where  PresetFlag='0' order by CoreId", null);
            if (dtCurrent == null || dtCurrent.Rows.Count == 0)
            {
                errMsg = $"无法新增预设工单,当前工单序列记录不存在";
                return false;
            }
            if (dtCurrent.Rows.Count < 1)
            {
                errMsg = $"无法新增预设工单,【工单_序列号表】当前工单的数据行数少于一行,实际行数【{dtCurrent.Rows.Count }】";
                return false;
            }
            //工单顺序号
            int planSequence = Convert.ToInt32(dtCurrent.Rows[0]["PlanSequence"]);
            int maxSequence = planSequence;
            //如果最大顺序号 小于 方案周期工单个数。则下一个顺序号加1,否则从头开始
            if (maxSequence < cycleCount)
            {
                planSequence = maxSequence + 1;
            }
            else
            {
                planSequence = 1;
            }
            //下一个工单信息【下一个序号】
            DataRow drNextPlanDetail = GetPlanDetailBySequence(planSequence);
            if (drNextPlanDetail == null)
            {
                errMsg = $"无法新增预设工单,没有找到当前有效的工单方案详细【不存在有效的并且序号为【{planSequence}】的工单方案记录】";
                return false;
            }
            InsertPresetOrderSequence(drNextPlanDetail, Convert.ToInt32(drNextPlanDetail["RemainderCount"]));
            return true;
        }

        /// <summary>
        /// 用于排他锁,确保在多线程调用接口时,不会同时调用
        /// </summary>
        static int lockedValue = 0;
        /// <summary>
        /// 获取当前工单信息,以及 预设下一个工单信息,发送给PLC
        /// work_order_plan_sequence表要新增一行当前工单信息 和 一行预设工单信息。共新增2行记录
        /// 返回共有两行记录:当前工单 和 预设下一个工单
        /// </summary>
        /// <param name="errMsg">错误信息</param>
        /// <param name="dtSequenceAndPreset">需要发送给PLC的工单数据行:有两行,当前工单 和 预设工单</param>
        /// <returns></returns>
        public static bool GetNextAndPresetWorkOrder(out string errMsg, out DataTable dtSequenceAndPreset)
        {
            //添加锁
            while (Interlocked.Exchange(ref lockedValue, 1) != 0)
            {
                //此循环用于等待当前捕获current的线程执行结束
                Thread.SpinWait(20);
            }
            errMsg = "";
            dtSequenceAndPreset = new DataTable("NextAndPreset");
            try
            {
                //方案周期工单个数
                object objCycleCount = SqliteAssist.GetScalar("select CycleCount from work_order_plan where Enabled=1", null);
                int cycleCount;
                int.TryParse(Convert.ToString(objCycleCount), out cycleCount);
                if (cycleCount <= 0)
                {
                    errMsg = $"没有找到当前有效的工单方案【方案周期工单个数未配置 cycleCount={cycleCount}】";
                    //释放锁
                    Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
                    return false;
                }
                //这里使用工单_序列号表逻辑,该表只有一行,执行:清除【work_order_plan_sequence】表的所有数据行,添加一条新的工单号
                //两个工单编号work_order_plan_detail.OrderCode相同则认为是同一工单,则表work_order_plan_detail的同一工单剩余总量都更新掉
                //第一步:读取【工单_序列号表】
                DataTable dtSequence = SqliteAssist.GetDataTable("select * from work_order_plan_sequence where PresetFlag=0  order by CoreId", null);
                if (dtSequence == null || dtSequence.Rows.Count == 0)
                {
                    //如果【工单_序列号表】没有数据行,则新增两条有效的并且序号为一的工单方案记录【work_order_plan_detail的Enabled=1 并且 PlanSequence=1】
                    DataRow drPlanDetail = GetPlanDetailBySequence(1);
                    if (drPlanDetail == null)
                    {
                        errMsg = $"没有找到当前有效的工单方案详细【不存在有效的并且序号为【一】的工单方案记录】";
                        //释放锁
                        Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
                        return false;
                    }
                    //剩余数量
                    int remainderCount = Convert.ToInt32(drPlanDetail["RemainderCount"]);
                    if (remainderCount <= 0)
                    {
                        errMsg = $"无法发放工单【一】,无剩余工单【{drPlanDetail["OrderCode"]}】数量【剩余数量不存在或者不大于0.RemainderCount={remainderCount}】";
                        //释放锁
                        Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
                        return false;
                    }
                    //先清除所有,然后增加两条记录
                    SqliteAssist.ExecuteCommand("delete from work_order_plan_sequence", null);
                    InsertNewOrderSequence(drPlanDetail, remainderCount - 1);
                    //work_order_plan_detail的同一工单剩余总量都更新掉,剩余数量减去1
                    UpdateWorkOrderRemainder(drPlanDetail["OrderCode"].ToString(), remainderCount - 1);
                }
                else //【工单_序列号表】存在数据行
                {
                    //工单顺序号
                    int planSequence = Convert.ToInt32(dtSequence.Rows[0]["PlanSequence"]);
                    int maxSequence = planSequence;
                    //如果最大顺序号 小于 方案周期工单个数。则下一个顺序号加1,否则从头开始
                    if (maxSequence < cycleCount)
                    {
                        planSequence = maxSequence + 1;
                    }
                    else
                    {
                        planSequence = 1;
                    }
                    //下一个工单信息【下一个序号】
                    DataRow drPlanDetail = GetPlanDetailBySequence(planSequence);
                    if (drPlanDetail == null)
                    {
                        errMsg = $"没有找到当前有效的工单方案详细一【不存在有效的并且序号为【{planSequence}】的工单方案记录】";
                        //释放锁
                        Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
                        return false;
                    }
                    //剩余数量
                    int remainderCount = Convert.ToInt32(drPlanDetail["RemainderCount"]);
                    string orderCode = drPlanDetail["OrderCode"].ToString();
                    if (remainderCount <= 0)
                    {
                        errMsg = $"无法发放工单【一】,无剩余工单【{orderCode}】数量【剩余数量不存在或者不大于0.RemainderCount={remainderCount}】";
                        //释放锁
                        Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
                        return false;
                    }
                    //先清除所有,然后增加一条记录
                    SqliteAssist.ExecuteCommand("delete from work_order_plan_sequence", null);
                    InsertNewOrderSequence(drPlanDetail, remainderCount - 1);
                    //work_order_plan_detail的同一工单剩余总量都更新掉
                    UpdateWorkOrderRemainder(orderCode, remainderCount - 1);
                }

                //新增一条 预设下一个工单信息
                if (!AddPresetWorkOrderSequence(cycleCount, out errMsg))
                {
                    //释放锁
                    Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
                    return false;
                }

                //获得新增的 当前工单_序列 和 预设工单序列 共有两行,用于写数据给PLC
                dtSequenceAndPreset = SqliteAssist.GetDataTable("select * from work_order_plan_sequence order by CoreId", null);
            }
            catch (Exception ex)
            {
                errMsg = $"发放当前工单和预设工单失败:{ex.Message}";
                //释放锁
                Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
                return false;
            }

            //释放锁
            Interlocked.Exchange(ref lockedValue, 0);//将current重置为0
            return true;
        }

        /// <summary>
        /// 刷新当前工单 和 预设下一工单
        /// </summary>
        /// <param name="EventRefreshWorkOrder">传入当前工单和下一工单元组的委托、事件、方法</param>
        public static void RefreshCurrentAndNextWorkOrder(Action<Tuple<string, string, string, string>, Tuple<string, string, string, string>> EventRefreshWorkOrder)
        {
            //刷新当前工单 和 预设工单work_order_plan_sequence
            //获得新增的 当前工单_序列 和 预设工单序列 共有两行,
            DataTable dtSequence = SqliteAssist.GetDataTable("select * from work_order_plan_sequence order by CoreId", null);
            if (dtSequence == null || dtSequence.Rows.Count < 2)
            {
                return;
            }
            DataRow drSequence = dtSequence.Rows[0];
            DataRow drPreset = dtSequence.Rows[1];
            string WorkOrderCode = drSequence["OrderCode"].ToString();
            string RemainderCount = drSequence["RemainderCount"].ToString();
            string PlanSequence = drSequence["PlanSequence"].ToString();
            string WorkOrderCategory = drSequence["WorkOrderCategory"].ToString();

            //预设工单信息
            string NextWorkOrderCode = drPreset["OrderCode"].ToString();
            string NextRemainderCount = drPreset["RemainderCount"].ToString();
            string NextPlanSequence = drPreset["PlanSequence"].ToString();
            string NextWorkOrderCategory = drPreset["WorkOrderCategory"].ToString();
            EventRefreshWorkOrder?.Invoke(Tuple.Create(WorkOrderCode, RemainderCount, PlanSequence, WorkOrderCategory),
                Tuple.Create(NextWorkOrderCode, NextRemainderCount, NextPlanSequence, NextWorkOrderCategory));
        }
    }
}

Guess you like

Origin blog.csdn.net/ylq1045/article/details/119707374