diff --git a/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs b/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs index ba90201..f7f1671 100644 --- a/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs +++ b/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs @@ -51,7 +51,7 @@ public class WorkOrderProgressController : BaseController } /// - /// 工单list (未完成的) + /// 工单list (未完成的) /// /// /// @@ -107,6 +107,29 @@ public class WorkOrderProgressController : BaseController return SUCCESS(response); } + + //TODO 暂停某个工单 + [HttpGet("pause_workorder")] + public IActionResult PauseWorkOrder(string workorder) + { + if (string.IsNullOrEmpty(workorder)) return SUCCESS(null); + var response = workorderProgressService.PauseWorkOrder(workorder); + + return SUCCESS(response); + } + + + //TODO 恢复某个工单 + [HttpGet("recover_workorder")] + public IActionResult RecoverWorkOrder(string workorder) + { + if (string.IsNullOrEmpty(workorder)) return SUCCESS(null); + var response = workorderProgressService.RecoverWorkOrder(workorder); + + return SUCCESS(response); + } + + //TODO 完成某一个工单 [HttpGet("finish_workorder")] public IActionResult FinishWorkOrder(string workorder) @@ -117,6 +140,22 @@ public class WorkOrderProgressController : BaseController return SUCCESS(response); } + /// + /// TODO 完成 工单 + /// + /// + /// - /// TODO 完成 工单 - /// - /// - /// + /// 生产工单 + /// + [SugarTable("pro_workorder_status")] + public class ProWorkorderStatus + { + + /// + /// + /// + [SugarColumn(ColumnName = "id", IsPrimaryKey = true)] + public int Id { get; set; } + + + /// + /// 工单号 + /// + [SugarColumn(ColumnName = "fk_workorder_code")] + public string FkWorkorderCode { get; set; } + + + /// + /// 状态(1 init,2 start 3 end 4 pause) + /// + [SugarColumn(ColumnName = "status")] + public int Status { get; set; } + + + + /// + /// 状态更改时间 + /// + [SugarColumn(ColumnName = "change_time")] + public DateTime ChangeTime { get; set; } + + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "created_time")] + public DateTime CreatedTime { get; set; } + } + +} diff --git a/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs b/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs index b178760..2d2893a 100644 --- a/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs +++ b/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs @@ -1,12 +1,13 @@ -using System; +using DOAN.Model.JobKanban; +using DOAN.Model.mes.echarts; +using DOAN.Model.MES.base_; +using DOAN.Model.MES.product; +using DOAN.Model.MES.product.Dto; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using DOAN.Model.JobKanban; -using DOAN.Model.MES.base_; -using DOAN.Model.MES.product; -using DOAN.Model.MES.product.Dto; namespace DOAN.Service.JobKanban.IService { public interface IWorkorderProgressService @@ -26,6 +27,10 @@ namespace DOAN.Service.JobKanban.IService KanbanInfo GetKanbanNum(DateTime today, string line_code, string group_code); Task StartWorkOrder(string workorder); + + + int PauseWorkOrder(string workorder); + int RecoverWorkOrder(string workorder); int FinishWorkOrder(string workorder); ProWorkorder GetProductingWorkorder(string line_code, DateTime handleDate); int AddLabelLog(string labelContext, string workOrder); @@ -44,5 +49,8 @@ namespace DOAN.Service.JobKanban.IService List GetWorkOrderScanCodeInfo(string workorder); bool SwitchWorkOrderCheckLabel(string pre_workorder); + + + EchartsOptions GetHourlyProduction(string groupCode); } } diff --git a/DOAN.Service/JobKanban/WorkorderProgressService.cs b/DOAN.Service/JobKanban/WorkorderProgressService.cs index 0a50322..1df05ec 100644 --- a/DOAN.Service/JobKanban/WorkorderProgressService.cs +++ b/DOAN.Service/JobKanban/WorkorderProgressService.cs @@ -1,6 +1,8 @@ using DOAN.Infrastructure.Helper; using DOAN.Model.JobKanban; +using DOAN.Model.mes.echarts; using DOAN.Model.MES.base_; +using DOAN.Model.MES.group; using DOAN.Model.MES.mm; using DOAN.Model.MES.product; using DOAN.Model.MES.product.Dto; @@ -14,6 +16,7 @@ using Mapster; using NPOI.SS.Formula.Functions; using Org.BouncyCastle.Asn1; using SqlSugar.SplitTableExtensions; +using System.Data; using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -162,59 +165,69 @@ public class WorkorderProgressService : BaseService, IWorkorderPro //TODO 拼接MQTT消息,发送给设备,工单信息和配方信息 var spec = handleWorkorder.Specification; - var RecipeMesg = Context.Queryable() - .LeftJoin((rpr, r) => rpr.RecipeCode == r.RecipeCode) - .Where((rpr, r) => SqlFunc.Like(spec, rpr.RecipeCode + "%")) - .Where((rpr, r) => r.Status == 1) - .Select((rpr, r) => new - { - RecipeCode = r.RecipeCode, - Version = r.Version, - ParamList = SqlFunc.Subqueryable() - .Where(it => it.RecipeCode == r.RecipeCode && it.Version == r.Version) - .ToList(), - // 添加匹配长度用于排序 - MatchLength = rpr.RecipeCode.Length - }) - .MergeTable() - .OrderByDescending(x => x.MatchLength) // 按匹配长度降序排列 - .First(); // 取第一个(匹配最长的) + //var RecipeMesg = Context.Queryable() + // .LeftJoin((rpr, r) => rpr.RecipeCode == r.RecipeCode) + // .Where((rpr, r) => SqlFunc.Like(spec, rpr.RecipeCode + "%")) + // .Where((rpr, r) => r.Status == 1) + // .Select((rpr, r) => new + // { + // RecipeCode = r.RecipeCode, + // Version = r.Version, + // ParamList = SqlFunc.Subqueryable() + // .Where(it => it.RecipeCode == r.RecipeCode && it.Version == r.Version) + // .ToList(), + // // 添加匹配长度用于排序 + // MatchLength = rpr.RecipeCode.Length + // }) + // .MergeTable() + // .OrderByDescending(x => x.MatchLength) // 按匹配长度降序排列 + // .First(); // 取第一个(匹配最长的) // 合并两个对象为匿名对象 var combinedObject = new { WorkorderInfo = handleWorkorder, - RecipeInfo = RecipeMesg + // RecipeInfo = RecipeMesg }; string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(combinedObject, Newtonsoft.Json.Formatting.Indented); //插入配方派发日志 - if (RecipeMesg != null && !string.IsNullOrEmpty(RecipeMesg.RecipeCode)) - { - PfRecipeIssueLog pfRecipeIssueLog = new PfRecipeIssueLog(); - pfRecipeIssueLog.RecipeCode = RecipeMesg.RecipeCode; - pfRecipeIssueLog.Version = RecipeMesg.Version; - pfRecipeIssueLog.IssueTime = DateTime.Now; - pfRecipeIssueLog.Workorder = handleWorkorder.Workorder; - pfRecipeIssueLog.Productcode = handleWorkorder.ProductionCode; - pfRecipeIssueLog.Productname = handleWorkorder.ProductionName; - pfRecipeIssueLog.CreatedBy = "PDA"; - pfRecipeIssueLog.CreatedTime = DateTime.Now; - Context.Insertable(pfRecipeIssueLog).ExecuteCommand(); + //if (RecipeMesg != null && !string.IsNullOrEmpty(RecipeMesg.RecipeCode)) + //{ + // PfRecipeIssueLog pfRecipeIssueLog = new PfRecipeIssueLog(); + // pfRecipeIssueLog.RecipeCode = RecipeMesg.RecipeCode; + // pfRecipeIssueLog.Version = RecipeMesg.Version; + // pfRecipeIssueLog.IssueTime = DateTime.Now; + // pfRecipeIssueLog.Workorder = handleWorkorder.Workorder; + // pfRecipeIssueLog.Productcode = handleWorkorder.ProductionCode; + // pfRecipeIssueLog.Productname = handleWorkorder.ProductionName; + // pfRecipeIssueLog.CreatedBy = "PDA"; + // pfRecipeIssueLog.CreatedTime = DateTime.Now; + // Context.Insertable(pfRecipeIssueLog).ExecuteCommand(); - } + //} MqttHelper _mqttHelper = new MqttHelper("192.168.50.163", 1883); // 连接 - await _mqttHelper.ConnectAsync(); + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2)); // 总超时10秒 + var connectTask = _mqttHelper.ConnectAsync(); + var timeoutTask = Task.Delay(5000, cts.Token); // 5秒连接超时 + + var completedTask = await Task.WhenAny(connectTask, timeoutTask); + if (completedTask == timeoutTask) + { + throw new TimeoutException("连接MQTT服务器超时"); + } + await connectTask; // 确保没有异常 // 发送多条消息 - string line = handleWorkorder.LineCode ==null?"-1": handleWorkorder.LineCode; - await _mqttHelper.PublishMessageAsync("MES/Workorder/Start/handleWorkorder/"+ line, jsonString); + string line = handleWorkorder.LineCode == null ? "-1" : handleWorkorder.LineCode; + await _mqttHelper.PublishMessageAsync("MES/Workorder/Start/handleWorkorder/" + line, jsonString); // 断开连接 await _mqttHelper.DisconnectAsync(); @@ -260,7 +273,7 @@ public class WorkorderProgressService : BaseService, IWorkorderPro } else { - + } } @@ -272,13 +285,91 @@ public class WorkorderProgressService : BaseService, IWorkorderPro } } + /// + /// 暂停工单 + /// + /// + /// + public int PauseWorkOrder(string workorder) + { + int result = 0; + UseTran2(() => + { + ProWorkorderStatus proWorkorderStatus = new ProWorkorderStatus(); + proWorkorderStatus.FkWorkorderCode = workorder; + proWorkorderStatus.Status = 4; + proWorkorderStatus.ChangeTime = DateTime.Now; + proWorkorderStatus.CreatedTime = DateTime.Now; + + Context.Insertable(proWorkorderStatus).ExecuteCommand(); + + + result = Context.Updateable() + .SetColumns(it => it.Status == 4) + .SetColumns(it => it.EndTime == DateTime.Now) + .Where(it => it.Workorder == workorder).ExecuteCommand(); + + }); + return result; + } + + + /// + /// 恢复工单 + /// + /// + /// + public int RecoverWorkOrder(string workorder) + { + int result = 0; + UseTran2(() => + { + ProWorkorderStatus proWorkorderStatus = new ProWorkorderStatus(); + proWorkorderStatus.FkWorkorderCode = workorder; + proWorkorderStatus.Status = 5; + proWorkorderStatus.ChangeTime = DateTime.Now; + proWorkorderStatus.CreatedTime = DateTime.Now; + + Context.Insertable(proWorkorderStatus).ExecuteCommand(); + + + result = Context.Updateable() + .SetColumns(it => it.Status == 5) + .SetColumns(it => it.EndTime == DateTime.Now) + .Where(it => it.Workorder == workorder).ExecuteCommand(); + + }); + return result; + } + /// + /// 工单完成 + /// + /// + /// + public int FinishWorkOrder(string workorder) { - return Context.Updateable() - .SetColumns(it => it.Status == 3) - .SetColumns(it => it.EndTime == DateTime.Now) - .Where(it => it.Workorder == workorder).ExecuteCommand(); + int result = 0; + UseTran2(() => + { + ProWorkorderStatus proWorkorderStatus = new ProWorkorderStatus(); + proWorkorderStatus.FkWorkorderCode = workorder; + proWorkorderStatus.Status = 3; + proWorkorderStatus.ChangeTime = DateTime.Now; + proWorkorderStatus.CreatedTime = DateTime.Now; + + + Context.Insertable(proWorkorderStatus).ExecuteCommand(); + + result = Context.Updateable() + .SetColumns(it => it.Status == 3) + .SetColumns(it => it.EndTime == DateTime.Now) + .Where(it => it.Workorder == workorder).ExecuteCommand(); + + }); + return result; } + public ProWorkorder GetProductingWorkorder(string line_code, DateTime handleDate) { @@ -496,4 +587,149 @@ public class WorkorderProgressService : BaseService, IWorkorderPro return false; } } + + public EchartsOptions GetHourlyProduction(string groupCode) + { + + EchartsOptions echartsOptions = new EchartsOptions(); + + echartsOptions.Title = new EchartsTitle("今日各组实时完工数与计划完工柱状图", "获取今日各组实时每小时完工数柱状图"); + + //横轴 8:00 20:00 每60分钟累加一次 + // 1.获取这个班组工作时间 + GroupShift Shifts = Context.Queryable().LeftJoin((sc, sh) => sc.FkShift == sh.Id) + .Where((sc, sh) => sc.ScheduleDate == DateTime.Today && sc.GroupCode == groupCode).Select((sc, sh) => sh).First(); + if (Shifts == null) + { + return null; + } + // 处理每小时间隔 + int intervals = (int)Math.Ceiling((Shifts.EndTime - Shifts.StartTime).Value.TotalMinutes / 60); + intervals++; + // 获取今天的日期 + DateTime today = DateTime.Today; + // 定义一个表示8小时的时间间隔 + TimeSpan eightOClock = new TimeSpan(Shifts.StartTime.Value.Hour, Shifts.StartTime.Value.Minute, Shifts.StartTime.Value.Second); + // 将今天的日期与8点的时间间隔相加 + DateTime todayAtEight = today + eightOClock; + DateTime[] dateTimeArray = new DateTime[intervals]; + for (int i = 0; i < intervals; i++) + { + TimeSpan spanItem = new TimeSpan(0, 10 * i, 0);// 60分钟间隔 + DateTime TodayItem = todayAtEight + spanItem; + dateTimeArray[i] = TodayItem; + } + EchartsXAxis XAxis = new EchartsXAxis(); + XAxis.Data = dateTimeArray.Select(it => it.ToString("HH:mm")).ToList(); + echartsOptions.XAxis = XAxis; + //2 系列值-这组 今日实时完成数(60分钟) + EchartsSeries echartsSeries = new EchartsSeries(); + echartsSeries.Name = groupCode + "组今日实时完成数(每60分钟)"; + echartsSeries.Type = "bar"; + List echartsSeriesDatas = new List(); + //UNIX_TIMESTAMP(timestamp) 把 timestamp 转换为自 Unix 纪元以来的秒数。 + //FLOOR(... / 600) 将该时间戳除以600(10分钟的秒数),然后向下取整到最接近的整数。 + //*600 再次乘以600,以获得每个10分钟周期开始的时间戳。 + //FROM_UNIXTIME(...) 将处理后的时间戳转换回日期时间格式,以便在结果集中显示。 + + string year = DateTime.Today.Year.ToString("D4"); + string month = DateTime.Today.Month.ToString("D2"); + string day = "01"; + + string sql = "SELECT FROM_UNIXTIME( FLOOR( UNIX_TIMESTAMP( d.created_time ) / 3600 ) * 3600 ) AS time_period, COUNT(*) AS count FROM " + + $"pro_workorder AS r RIGHT JOIN pro_reportwork_detail_{year}{month}{day} AS d ON r.workorder = d.workorder " + + "WHERE r.workorder_date = CURDATE() AND r.group_code= @groupCode " + + " GROUP BY FLOOR( UNIX_TIMESTAMP( d.created_time ) / 600 ) ORDER BY time_period"; + + + + DataTable result = Context.Ado.GetDataTable(sql, new { groupCode = groupCode, }); + int sum = 0; + + + foreach (DataRow row in result.Rows) + { + // DateTime value =(DateTime)row["time_period"]; + DateTime value = row["time_period"] != DBNull.Value ? Convert.ToDateTime(row["time_period"]) : DateTime.MinValue; + int count = Convert.ToInt32(row["count"]); + + // sum = sum + count; + EchartsSeriesData echartsSeriesData = new EchartsSeriesData() + { + Name = value.ToString("HH:mm"), + Value = count + }; + echartsSeriesDatas.Add(echartsSeriesData); + } + int currentNum = Array.IndexOf(XAxis.Data.ToArray(), echartsSeriesDatas.Select(it => it.Name).LastOrDefault()); + for (int i = 0; i < currentNum; i++) + { + int point = 0; + foreach (var item in echartsSeriesDatas) + { + + if (item.Name == XAxis.Data[i]) + { + continue; + } + point++; + + } + + if (point == echartsSeriesDatas.Count()) + { + // 获取前一个时间段产量 + decimal productNum = 0; + if (i >= 1) + { + productNum = echartsSeriesDatas.Where(it => it.Name == XAxis.Data[i - 1]).Select(it => it.Value).FirstOrDefault(); + } + else + { + productNum = 0; + } + + + echartsSeriesDatas.Add(new EchartsSeriesData() { Name = XAxis.Data[i], Value = productNum }); + } + } + echartsSeries.Data = echartsSeriesDatas.OrderBy(it => it.Name).ToList(); + echartsOptions.Series.Add(echartsSeries); + + //3 系列值-这组 今日计划累计完成数(每60分钟) + //获取今天这个组的所有工单 + List ProWorkorderList = Context.Queryable().Where(it => it.GroupCode == groupCode && it.WorkorderDate == DateTime.Today).ToList(); + + //求出总产量需要多少s + int allSecends = 0; + foreach (var item in ProWorkorderList) + { + int num = (item.DeliveryNum ?? 0) * (item.Beat ?? 0); + allSecends = num + allSecends; + } + + // 将节拍转为每60分钟产量 + int allproduct = ProWorkorderList.Sum(it => it.DeliveryNum ?? 0); + int productOfeachTenMin = allproduct / (allSecends / (60 * 60)); + + EchartsSeries echartsSeries02 = new EchartsSeries(); + echartsSeries02.Name = groupCode + "组今日计划累计完成数(每60分钟)"; + echartsSeries02.Type = "bar"; + List echartsSeriesData02s = new List(); + //int all_plan_product = 0; + for (int i = 0; i < dateTimeArray.Length; i++) + { + + EchartsSeriesData echartsSeriesData = new EchartsSeriesData(); + echartsSeriesData.Name = dateTimeArray[i].ToString("HH:mm"); + echartsSeriesData.Value = productOfeachTenMin; + echartsSeriesData02s.Add(echartsSeriesData); + //all_plan_product = all_plan_product + productOfeachTenMin; + } + echartsSeries02.Data = echartsSeriesData02s; + echartsOptions.Series.Add(echartsSeries02); + + return echartsOptions; + + } } \ No newline at end of file diff --git a/MDM/Controllers/Session/SessionManagerController.cs b/MDM/Controllers/Session/SessionManagerController.cs index 1798292..bf29de1 100644 --- a/MDM/Controllers/Session/SessionManagerController.cs +++ b/MDM/Controllers/Session/SessionManagerController.cs @@ -74,7 +74,7 @@ namespace MDM.Controllers.Session public IActionResult MiddleStationApply(string OperationCode, string ProcessCode) { - Dictionary> result = _ISessionManagerService.MiddleProcessFlowDistribution(OperationCode, ProcessCode); + List result = _ISessionManagerService.MiddleProcessFlowDistribution(OperationCode, ProcessCode); return SUCCESS(result); } @@ -90,6 +90,9 @@ namespace MDM.Controllers.Session } + + + //TODO 完工申请 /// /// 完工申请 diff --git a/MDM/Models/Flow/FlowEnums.cs b/MDM/Models/Flow/FlowEnums.cs index 2e58372..52f9df3 100644 --- a/MDM/Models/Flow/FlowEnums.cs +++ b/MDM/Models/Flow/FlowEnums.cs @@ -169,6 +169,16 @@ namespace MDM.Models.Flow /// 未知状态,,禁止出站 /// UnknownStatus = -10, + /// + /// 扫码流程未完成,禁止出站 + /// + + ScanFlowNotCompleted = -11, + + /// + /// 参数采集未完成,禁止出站 + /// + ParmetersCollectNotCompleted = -12 } diff --git a/MDM/Models/Session/ProductLifecycle.cs b/MDM/Models/Product/ProductLifecycle.cs similarity index 100% rename from MDM/Models/Session/ProductLifecycle.cs rename to MDM/Models/Product/ProductLifecycle.cs diff --git a/MDM/Models/Session/ProductPassstationRecord.cs b/MDM/Models/Product/ProductPassstationRecord.cs similarity index 100% rename from MDM/Models/Session/ProductPassstationRecord.cs rename to MDM/Models/Product/ProductPassstationRecord.cs diff --git a/MDM/Models/Session/ProductProcessData.cs b/MDM/Models/Product/ProductProcessData.cs similarity index 100% rename from MDM/Models/Session/ProductProcessData.cs rename to MDM/Models/Product/ProductProcessData.cs diff --git a/MDM/Models/Product/ProductProcessParameters.cs b/MDM/Models/Product/ProductProcessParameters.cs new file mode 100644 index 0000000..47ba467 --- /dev/null +++ b/MDM/Models/Product/ProductProcessParameters.cs @@ -0,0 +1,151 @@ +using MathNet.Numerics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MDM.Models.Session +{ + /// + /// 生产过程参数 + /// + [SugarTable("product_process_parameters")] + public class ProductProcessParameters + { + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 工单号 + /// + [SugarColumn(ColumnName = "workorder", Length = 50)] + public string Workorder { get; set; } + + /// + /// 产品SN + /// + [SugarColumn(ColumnName = "product_SN", Length = 50)] + public string ProductSN { get; set; } + + /// + /// 工艺路线编码 + /// + [SugarColumn(ColumnName = "routingCode", Length = 50)] + public string RoutingCode { get; set; } + + /// + /// 工序号 + /// + [SugarColumn(ColumnName = "operationCode", Length = 50)] + public string OperationCode { get; set; } + + /// + /// 流程code + /// + [SugarColumn(ColumnName = "flowCode", Length = 50)] + public string FlowCode { get; set; } + + /// + /// 产线code + /// + [SugarColumn(ColumnName = "productlinebodyCode", Length = 50)] + public string ProductlinebodyCode { get; set; } + + /// + /// 工站code + /// + [SugarColumn(ColumnName = "workstationCode", Length = 50)] + public string WorkstationCode { get; set; } + + /// + /// 参数名称,如:温度、压力、时间 + /// + [SugarColumn(ColumnName = "parameter_code")] + public string ParameterCode { get; set; } + + /// + /// 显示名称(用于UI展示,可和name一样) + /// + [SugarColumn(ColumnName = "parameter_name", Length = 100)] + public string ParameterName { get; set; } + + /// + /// plc点位 + /// + [SugarColumn(ColumnName = "plc_point", Length = 50)] + public string PlcPoint { get; set; } + + /// + /// 参数描述,如:模具温度,用于热压工序 + /// + [SugarColumn(ColumnName = "description", ColumnDataType = "text")] + public string Description { get; set; } + + /// + /// 数据类型:FLOAT, INT, STRING, BOOL, AI(模拟量输入)等 + /// + [SugarColumn(ColumnName = "data_type", Length = 50, IsNullable = false)] + public string DataType { get; set; } = "FLOAT"; + + /// + /// 单位,如:℃、MPa、秒、mm + /// + [SugarColumn(ColumnName = "unit", Length = 20)] + public string Unit { get; set; } + + /// + /// 标准/目标值(如目标温度 200.0 ℃) + /// + [SugarColumn(ColumnName = "standard_value" )] + public decimal? StandardValue { get; set; } + + /// + /// 最小允许值(用于报警/校验) + /// + [SugarColumn(ColumnName = "min_value")] + public decimal? MinValue { get; set; } + + /// + /// 最大允许值(用于报警/校验) + /// + [SugarColumn(ColumnName = "max_value")] + public decimal? MaxValue { get; set; } + + /// + /// 最小允许值(用于报警/校验) + /// + [SugarColumn(ColumnName = "result")] + public int? Result { get; set; } + + + + /// + /// 创建人 + /// + [SugarColumn(ColumnName = "createdby", Length = 50)] + public string Createdby { get; set; } + + /// + /// 更新人 + /// + [SugarColumn(ColumnName = "updatedby", Length = 50)] + public string Updatedby { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(ColumnName = "created_time")] + public DateTime? CreatedTime { get; set; } + + /// + /// 更新时间 + /// + [SugarColumn(ColumnName = "updated_time")] + public DateTime? UpdatedTime { get; set; } + } + +} diff --git a/MDM/Models/Product/ProductScanRecord.cs b/MDM/Models/Product/ProductScanRecord.cs new file mode 100644 index 0000000..bd975ce --- /dev/null +++ b/MDM/Models/Product/ProductScanRecord.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MDM.Models.Session +{ + [SugarTable("product_scan_records")] + public class ProductScanRecords + { + /// + /// 主键ID + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = false)] + public long Id { get; set; } + + /// + /// 工单号 + /// + [SugarColumn(ColumnName = "workorder", Length = 50)] + public string Workorder { get; set; } + + /// + /// 过程码 + /// + [SugarColumn(ColumnName = "product_SN", Length = 50)] + public string ProductSN { get; set; } + + /// + /// 工艺路线code + /// + [SugarColumn(ColumnName = "routingcode", Length = 50)] + public string Routingcode { get; set; } + + /// + /// 工序code + /// + [SugarColumn(ColumnName = "operationCode", Length = 50)] + public string OperationCode { get; set; } + + /// + /// 流程code + /// + [SugarColumn(ColumnName = "flowCode", Length = 50)] + public string FlowCode { get; set; } + + /// + /// 实际扫到码 + /// + [SugarColumn(ColumnName = "actual_scan_code", Length = 100)] + public string ActualScanCode { get; set; } + + /// + /// 期望扫到的码 + /// + [SugarColumn(ColumnName = "expected_code", Length = 100)] + public string ExpectedCode { get; set; } + + /// + /// 防错规则 + /// + [SugarColumn(ColumnName = "error_proof_rule_code", Length = 50)] + public string ErrorProofRuleCode { get; set; } + + /// + /// 扫码结果(OK,NG) + /// + [SugarColumn(ColumnName = "scan_result")] + public ScanResultEnum? ScanResult { get; set; } + + /// + /// 错误码代号 + /// + [SugarColumn(ColumnName = "error_code", Length = 50)] + public string ErrorCode { get; set; } + + /// + /// 错误信息 + /// + [SugarColumn(ColumnName = "error_message", Length = 255)] + public string ErrorMessage { get; set; } + + /// + /// 扫码时间 + /// + [SugarColumn(ColumnName = "scan_time")] + public DateTime? ScanTime { get; set; } + } + + /// + /// 扫码结果枚举 + /// + public enum ScanResultEnum + { + OK = 1, + NG = 0 + } +} diff --git a/MDM/Models/Session/ProductRunning.cs b/MDM/Models/Session/ProductRunning.cs deleted file mode 100644 index 5daad0b..0000000 --- a/MDM/Models/Session/ProductRunning.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MDM.Models.Session -{ - -} diff --git a/MDM/Models/Session/ProductScanRecord.cs b/MDM/Models/Session/ProductScanRecord.cs deleted file mode 100644 index 39e3b8c..0000000 --- a/MDM/Models/Session/ProductScanRecord.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MDM.Models.Session -{ - [SugarTable("product_scan_record")] - public class ProductScanRecord - { - /// - /// 主键ID - /// - [SugarColumn(IsPrimaryKey = true, IsIdentity = false)] // bigint类型,非自增 - public long Id { get; set; } - - /// - /// 实际扫到码 - /// - [SugarColumn(ColumnName = "actual_scan_code", Length = 100)] - public string ActualScanCode { get; set; } - - /// - /// 期望扫到的码 - /// - [SugarColumn(ColumnName = "expected_code", Length = 100)] - public string ExpectedCode { get; set; } - - /// - /// 扫码结果 (建议:0-失败,1-成功) - /// - [SugarColumn(ColumnName = "scan_result")] - public int? ScanResult { get; set; } - - /// - /// 错误码代号 - /// - [SugarColumn(ColumnName = "error_code", Length = 50)] - public string ErrorCode { get; set; } - - /// - /// 错误信息 - /// - [SugarColumn(ColumnName = "error_message", Length = 255)] - public string ErrorMessage { get; set; } - - /// - /// 扫码时间 - /// - [SugarColumn(ColumnName = "scan_time")] - public DateTime? ScanTime { get; set; } - } -} diff --git a/MDM/Services/Flows/CommonFlowService.cs b/MDM/Services/Flows/CommonFlowService.cs index 1895ae6..5580f23 100644 --- a/MDM/Services/Flows/CommonFlowService.cs +++ b/MDM/Services/Flows/CommonFlowService.cs @@ -1,4 +1,5 @@  +using DOAN.Model.MES.product; using DOAN.Model.MES.recipe; using DOAN.Model.MES.recipe.Dto; using Infrastructure.Attribute; @@ -10,6 +11,7 @@ using MDM.Models.Process; using MDM.Models.Session; using MDM.Service; using NPOI.SS.Formula.Functions; +using System.Text.Json; namespace MDM.Services.Flows @@ -190,19 +192,13 @@ namespace MDM.Services.Flows //TODO: 下发中间操作流程规则 - - - //TODO: 扫码流程 类型 [RIZOFlow(FlowType = "Scanningcode_flow")] - public List ScanningcodeFlow(string RoutingCode, string operationCode, string FlowCode) + public string ScanningcodeFlow(string RoutingCode, string operationCode, string FlowCode) { - - //获取这个工序d的扫码规则 - List ScanningFlowList = Context.Queryable().LeftJoin((f, m) => f.FkRoutingCode == m.FkRoutingCode && f.FkOperationCode == m.FkOperationCode && f.FlowCode == m.FkFlowCode) - - .Where((f, m) => f.FkRoutingCode == RoutingCode && f.FkOperationCode == operationCode && f.FlowTypeCode == "Scanningcode_flow"&&f.FlowCode== FlowCode) + List flowRuleList = Context.Queryable().LeftJoin((f, m) => f.FkRoutingCode == m.FkRoutingCode && f.FkOperationCode == m.FkOperationCode && f.FlowCode == m.FkFlowCode) + .Where((f, m) => f.FkRoutingCode == RoutingCode && f.FkOperationCode == operationCode && f.FlowTypeCode == "Scanningcode_flow" && f.FlowCode == FlowCode) .Select((f, m) => new ProcessOperationFlowMaterialParamter() { id = f.id, @@ -212,24 +208,35 @@ namespace MDM.Services.Flows MaterialCode = m.MaterialCode, MaterialName = m.MaterialName, UseErrorProofRuleCode = m.UseErrorProofRuleCode, - + }) .ToList(); - return ScanningFlowList; + var data = new + { + flowbase = new + { + routingCode = RoutingCode, + operationCode = operationCode, + flowCode = FlowCode + }, + flowRuleList + + }; + return System.Text.Json.JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); } //TODO: 数采流程 类型 (工位流程) [RIZOFlow(FlowType = "data_collect_flow")] - public List DataCollectFlow( string RoutingCode, string operationCode, string FlowCode, string ProductlinebodyCode, string WorkstationCode) + public string DataCollectFlow(string RoutingCode, string operationCode, string FlowCode, string ProductlinebodyCode, string WorkstationCode) { //获取这个工序d的扫码规则 - List DataCollectFlowList = Context.Queryable().LeftJoin + List flowRuleList = Context.Queryable().LeftJoin ((f, m) => f.FkRoutingCode == m.FkRoutingCode && f.FkOperationCode == m.FkOperationCode && f.FlowCode == m.FkFlowCode) .Where((f, m) => f.FkRoutingCode == RoutingCode && f.FkOperationCode == operationCode && f.FlowTypeCode == "Scanningcode_flow") - .Where((f,m)=>m.FkProductlinebodyCode== ProductlinebodyCode&&m.FkWorkstationCode== WorkstationCode&&m.FkFlowCode==FlowCode) + .Where((f, m) => m.FkProductlinebodyCode == ProductlinebodyCode && m.FkWorkstationCode == WorkstationCode && m.FkFlowCode == FlowCode) .Select((f, m) => new ProcessOperationWorkstationFlowCollectParameter() - { + { FkRoutingCode = f.FkRoutingCode, FkOperationCode = f.FkOperationCode, FkProductlinebodyCode = m.FkProductlinebodyCode, @@ -250,23 +257,34 @@ namespace MDM.Services.Flows DefaultValue = m.DefaultValue, IsRequired = m.IsRequired, Sequence = m.Sequence, - - - }) .ToList(); - return DataCollectFlowList; + var data = new + { + flowbase = new + { + routingCode = RoutingCode, + operationCode = operationCode, + productlinebodyCode = ProductlinebodyCode, + workstationCode = WorkstationCode, + flowCode = FlowCode + }, + flowRuleList + + }; + return System.Text.Json.JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); + } //TODO: 配方流程 类型 [RIZOFlow(FlowType = "recipe_distribute_flow")] - public List RecipeDistributeFlow(string RoutingCode, string operationCode, string FlowCode) + public string RecipeDistributeFlow(string RoutingCode, string operationCode, string FlowCode) { - return Context.Queryable() + var flowRuleList = Context.Queryable() .LeftJoin((refpr, ver) => refpr.RecipeCode == ver.RecipeCode) .LeftJoin((refpr, ver, param) => ver.RecipeCode == param.RecipeCode && ver.Version == param.Version) - .Where((refpr, ver, param) => refpr.FkFlowCode== FlowCode&&refpr.FkRoutingCode == RoutingCode && refpr.FkOperationCode == operationCode) + .Where((refpr, ver, param) => refpr.FkFlowCode == FlowCode && refpr.FkRoutingCode == RoutingCode && refpr.FkOperationCode == operationCode) .Select((refpr, ver, param) => new PfRecipeParametersDto { Id = param.Id, @@ -279,7 +297,18 @@ namespace MDM.Services.Flows StandardValue = param.StandardValue, Remark = param.Remark }).ToList(); - + var data = new + { + flowbase = new + { + routingCode = RoutingCode, + operationCode = operationCode, + flowCode = FlowCode + }, + flowRuleList + + }; + return System.Text.Json.JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); } @@ -289,69 +318,112 @@ namespace MDM.Services.Flows /// /// /// - //[RIZOFlow(FlowType = "common_outbound_flow")] - //public OutStationApplyResult CommonOutboundStationFlow(string operationCode, string processCode) - //{ - // // 参数验证 - // if (string.IsNullOrWhiteSpace(operationCode) || string.IsNullOrWhiteSpace(processCode)) - // { - // return OutStationApplyResult.InvalidParameters; - // } - // // 获取产品是正常件,完成件,返工件 - // var product = GetProductBySn(processCode); - // if (product == null) - // { - // // 产品未开工,不得出站 - // return OutStationApplyResult.ProductNotStartWork; - // } - // if (product.ProductStatus == 2) - // { - // // 产品已经生产完成,不得出站 - // return OutStationApplyResult.ProductCompleted; - // } - // //如果正常件 - // if (product.ProductStatus == 2) - // { - // // 此工序未入站,禁止出站 + [RIZOFlow(FlowType = "common_outbound_flow")] + public OutStationApplyResult CommonOutboundStationFlow(string operationCode, string processCode) + { + // 参数验证 + if (string.IsNullOrWhiteSpace(operationCode) || string.IsNullOrWhiteSpace(processCode)) + { + return OutStationApplyResult.InvalidParameters; + } - // // 此工序未完成中间流程,禁止出站 + // 1.获取产品是正常件,完成件,返工件 + ProductLifecycle productInfo = Context.Queryable() + .Where(p => p.ProductSN == processCode) + .First(); + if (productInfo == null) + { + // 产品未开工,不得出站 + RecordOutTrace(productInfo, operationCode, 1, ApplyStatusEnum.REJECTED, OutStationApplyResult.ProductNotStartWork.ToString(), "产品未开工,不得入站"); + return OutStationApplyResult.ProductNotStartWork; + } + if (productInfo.ProductStatus == 2) + { + // 产品已经生产完成,不得出站 + RecordOutTrace(productInfo, operationCode, 1, ApplyStatusEnum.REJECTED, OutStationApplyResult.ProductCompleted.ToString(), "产品已经生产完成,不得入站"); + return OutStationApplyResult.ProductCompleted; + } + if (productInfo.ProductStatus == 3) + { + // 返工件允许出站 + RecordOutTrace(productInfo, operationCode, 1, ApplyStatusEnum.SUCCESS, OutStationApplyResult.Success.ToString(), string.Empty); + return OutStationApplyResult.Success; + } + //检查扫码流程是否已经完成 + var ScanResults = Context.Queryable() + .LeftJoin((f, r) => f.FkFlowCode == r.FlowCode && f.FkRoutingCode == r.Routingcode && f.FkOperationCode == r.OperationCode) + .Where((f, r) => f.FkRoutingCode == productInfo.RoutingCode) + .Where((f, r) => f.FkOperationCode == operationCode) + .Select((f, r) => new { f.FkFlowCode, r.ScanResult }) + .ToList(); - // //插入出站记录 - // ProductPassstationRecord passstationRecord = new ProductPassstationRecord - // { - // Workorder = product.Workorder, - // Routingcode = product.RoutingCode, - // ProductSN = product.ProductSN, - // OperationCode = operationCode, - // ProuductStatus = 3, // 出站状态 - // OutStationTime = DateTime.Now, - // CreatedTime = DateTime.Now - // }; - // Context.Insertable(passstationRecord).ExecuteCommand(); - // return OutStationApplyResult.Success; - // } - // //如果,返工件 - // if (product.ProductStatus == 3) - // { - // //插入出站记录 - // ProductPassstationRecord passstationRecord = new ProductPassstationRecord - // { - // Workorder = product.Workorder, - // Routingcode = product.RoutingCode, - // ProductSN = product.ProductSN, - // OperationCode = operationCode, - // ProuductStatus = 4, // 出站状态 - // OutStationTime = DateTime.Now, - // CreatedTime = DateTime.Now - // }; - // Context.Insertable(passstationRecord).ExecuteCommand(); - // return OutStationApplyResult.Success; - // } + bool isFinish= ScanResults.Where(it => it.ScanResult == ScanResultEnum.NG||it.ScanResult==null).Any(); + + if (isFinish) + { + RecordOutTrace(productInfo, operationCode, 1, ApplyStatusEnum.REJECTED, OutStationApplyResult.ScanFlowNotCompleted.ToString(), "产品未开工,不得入站"); + return OutStationApplyResult.ScanFlowNotCompleted; + } + + //检查数采流程是否已经完成 + // 获取产线 + string LineCode = Context.Queryable().Where(it => it.Workorder == productInfo.Workorder).Select(it=>it.LineCode).First(); + + var DataCollectList = Context.Queryable() + .LeftJoin((f, p) => f.FkFlowCode == p.FlowCode && f.FkRoutingCode == p.RoutingCode && f.FkOperationCode == p.OperationCode && f.ParameterCode == p.ParameterCode) + .Where((f, p) => f.FkRoutingCode == productInfo.RoutingCode&&f.FkOperationCode== operationCode&&f.FkProductlinebodyCode==LineCode) + .Select((f, r) => new { f.FkFlowCode, r.Result }) + .ToList(); + + bool isFinish_ = DataCollectList.Where(it => it.Result == 0 || it.Result == null).Any(); + if (isFinish_) + { + RecordOutTrace(productInfo, operationCode, 1, ApplyStatusEnum.REJECTED, OutStationApplyResult.ParmetersCollectNotCompleted.ToString(), "产品未开工,不得入站"); + return OutStationApplyResult.ParmetersCollectNotCompleted; + } + //准许出站 + return OutStationApplyResult.Success; + } + + private void RecordOutTrace(ProductLifecycle product, string operationCode, int ProductionLifeStage, ApplyStatusEnum isPass, string rejectReasonCode, string rejectReasonDesc) + { + // 更新产品状态 + if (product != null) + { + Context.Updateable() + .SetColumns(it => new ProductLifecycle + { + ProductStatus = 1, // 设置为生产中状态 + UpdatedTime = DateTime.Now + }) + .Where(it => it.ProductSN == product.ProductSN) + .ExecuteCommand(); + } + + // 构建入站记录 + var passstationRecord = new ProductPassstationRecord + { + Workorder = product?.Workorder, + Routingcode = product?.RoutingCode, + ProductSN = product?.ProductSN ?? string.Empty, + OperationCode = operationCode, + ProductionLifeStage = ProductionLifeStage, // 生产中 + ApplyType = ApplyTypeEnum.OUT, + ApplyStatus = isPass, + RejectReasonCode = rejectReasonCode, + RejectReasonDesc = rejectReasonDesc, + InStationTime = DateTime.Now, + CreatedTime = DateTime.Now + }; + + Context.Insertable(passstationRecord).ExecuteCommand(); - // return OutStationApplyResult.UnknownStatus; - //} + + } + + } } diff --git a/MDM/Services/Session/IService/ISessionManagerService.cs b/MDM/Services/Session/IService/ISessionManagerService.cs index 4ae1620..6ae3919 100644 --- a/MDM/Services/Session/IService/ISessionManagerService.cs +++ b/MDM/Services/Session/IService/ISessionManagerService.cs @@ -15,7 +15,7 @@ namespace MDM.Services.Session.IService InStationApplyResult InStationApply(string OperationCode, string ProcessCode); - Dictionary> MiddleProcessFlowDistribution(string operationCode, string ProcessCode); + List MiddleProcessFlowDistribution(string operationCode, string ProcessCode); OutStationApplyResult OutStationApply(string OperationCode, string ProcessCode); diff --git a/MDM/Services/Session/SessionManagerService.cs b/MDM/Services/Session/SessionManagerService.cs index 11a65b7..69e3cba 100644 --- a/MDM/Services/Session/SessionManagerService.cs +++ b/MDM/Services/Session/SessionManagerService.cs @@ -135,19 +135,19 @@ namespace MDM.Services.Session /// /// /// - public Dictionary> MiddleProcessFlowDistribution(string operationCode, string ProcessCode) + public List MiddleProcessFlowDistribution(string operationCode, string ProcessCode) { var lifecycle = Context.Queryable() .Where(it => it.ProductSN == ProcessCode) .First(); - ProWorkorder proWorkorder= Context.Queryable().Where(it=>it.Workorder== lifecycle.Workorder).First(); - ProcessOperationWorkstationMapping processOperationWorkstationMapping= Context.Queryable().Where(it => it.FkRoutingCode == lifecycle.RoutingCode && it.FkOperationCode == operationCode && it.FkProductlinebodyCode == proWorkorder.LineCode).First(); + ProWorkorder proWorkorder = Context.Queryable().Where(it => it.Workorder == lifecycle.Workorder).First(); + ProcessOperationWorkstationMapping processOperationWorkstationMapping = Context.Queryable().Where(it => it.FkRoutingCode == lifecycle.RoutingCode && it.FkOperationCode == operationCode && it.FkProductlinebodyCode == proWorkorder.LineCode).First(); - Dictionary> keyValuePairs= new Dictionary>(); + List resultList = new List(); //TODO 获取这个工序的中间操作流程 - List operationFlows= Context.Queryable().Where(it => !it.FlowTypeCode.Contains("bound_flow")).ToList(); + List operationFlows = Context.Queryable().Where(it => !it.FlowTypeCode.Contains("bound_flow")).ToList(); - if(operationFlows!=null&& operationFlows.Count()>0) + if (operationFlows != null && operationFlows.Count() > 0) { // 已知程序集和类名 string assemblyName = "MDM.Services.Flows"; @@ -174,46 +174,46 @@ namespace MDM.Services.Session // 获取方法的参数信息 var parameters = method.GetParameters(); //工序流程 - if(parameters.Length==3) + if (parameters.Length == 3) { var result = method.Invoke(instance, new object[] { lifecycle.RoutingCode, operationCode, rizoFlow.FlowCode }); - if(result is List list) + if (result is string list) { - keyValuePairs.Add(rizoFlow.FlowCode, list); + resultList.Add(list); } } // 工位流程 if (parameters.Length == 5) { var result = method.Invoke(instance, new object[] { lifecycle.RoutingCode, operationCode, rizoFlow.FlowCode, processOperationWorkstationMapping.FkProductlinebodyCode, processOperationWorkstationMapping.FkWorkstationCode }); - if (result is List list) + if (result is string list) { - keyValuePairs.Add(rizoFlow.FlowCode, list); + resultList.Add(list); } } } } - + } } - + } - return keyValuePairs; + return resultList; } - /// - /// 出站申请 - /// - /// - /// - /// + /// + /// 出站申请 + /// + /// + /// + /// public OutStationApplyResult OutStationApply(string OperationCode, string ProcessCode) { @@ -293,6 +293,10 @@ namespace MDM.Services.Session { return -1; } + + + + return Context.Updateable() .SetColumns(it => new ProductLifecycle() {