最近做一个项目,混料生产需要自动下发当前工单。
整体逻辑是这样的:
混料生产三种类型(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));
}
}
}