diff --git a/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs b/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs index 5263767..7798a7c 100644 --- a/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs +++ b/DOAN.Admin.WebApi/Controllers/JobKanban/WorkOrderProgressController.cs @@ -44,7 +44,7 @@ public class WorkOrderProgressController : BaseController { if (string.IsNullOrEmpty(group_code) || string.IsNullOrEmpty(line_code) || handleDate == DateTime.MinValue) return SUCCESS(null); - handleDate= DOANConvertDateTime.ConvertLocalDate(handleDate); + handleDate = DOANConvertDateTime.ConvertLocalDate(handleDate); var response = workorderProgressService.GetReportWorkRecord(group_code, line_code, handleDate); return SUCCESS(response); @@ -113,7 +113,7 @@ public class WorkOrderProgressController : BaseController public IActionResult PauseWorkOrder(string workorder) { if (string.IsNullOrEmpty(workorder)) return SUCCESS(null); - var response = workorderProgressService.PauseWorkOrder(workorder); + var response = workorderProgressService.PauseWorkOrder(workorder); return SUCCESS(response); } @@ -192,7 +192,7 @@ public class WorkOrderProgressController : BaseController //TODO 防错并且报工 /// - /// 防错并且报工 + /// 防错并且报工 /// /// /// @@ -222,7 +222,7 @@ public class WorkOrderProgressController : BaseController public IActionResult GetWorkOrderProgress(string workorder) { if (string.IsNullOrEmpty(workorder)) throw new CustomException("workorder is null"); - + var response = workorderProgressService.GetWorkOrderProgress(workorder); return SUCCESS(response); } @@ -232,7 +232,7 @@ public class WorkOrderProgressController : BaseController public IActionResult GetWorkOrderTime(string workorder) { if (string.IsNullOrEmpty(workorder)) throw new CustomException("workorder is null"); - + var response = workorderProgressService.GetWorkOrderTime(workorder); return SUCCESS(response); } @@ -240,7 +240,7 @@ public class WorkOrderProgressController : BaseController //TODO 查询工单下的扫描条码信息 [HttpGet("get_scan_code_info")] - public IActionResult GetWorkOrderScanCodeInfo(string workorder) + public IActionResult GetWorkOrderScanCodeInfo(string workorder) { if (string.IsNullOrEmpty(workorder)) throw new CustomException("workorder is null"); var response = workorderProgressService.GetWorkOrderScanCodeInfo(workorder); @@ -267,5 +267,22 @@ public class WorkOrderProgressController : BaseController return SUCCESS(response); } + //TODO 当前工单上一个小时的完成率低于90%,给出提示信息; + /// + /// 结果 1 是 触发andon 0 是不触发andon + /// + /// + /// + /// + [HttpGet("get_last_hour_completion_rate")] + public IActionResult GetLastHourCompletionRate(string workorder) + { + if (string.IsNullOrEmpty(workorder)) throw new CustomException("workorder is null"); + var response = workorderProgressService.GetLastHourCompletionRate(workorder); + return SUCCESS(response); + } + + + } \ No newline at end of file diff --git a/DOAN.Admin.WebApi/Controllers/MES/andon/AndonInteractionController.cs b/DOAN.Admin.WebApi/Controllers/MES/andon/AndonInteractionController.cs index ad1ba44..68fcd22 100644 --- a/DOAN.Admin.WebApi/Controllers/MES/andon/AndonInteractionController.cs +++ b/DOAN.Admin.WebApi/Controllers/MES/andon/AndonInteractionController.cs @@ -56,6 +56,29 @@ namespace DOAN.WebApi.Controllers.MES.andon return SUCCESS(response); } + /// + /// 呼叫请求 SCREEN + /// + /// + /// + [HttpPost("Call_SCREEN")] + public IActionResult CallHandleSCREEN([FromBody] AndonAskQueryDto query) + { + if (query == null) + { + return SUCCESS(null); + + } + AndonFaultRecord record = query.Adapt(); + record.ToCreate(HttpContext); + var response = _andonInteractionService.CallHandleSCREEN(record); + + //TODO 通知到PDA 发消息 + _hubContext.Clients.All.SendAsync("Call", query); + + return SUCCESS(response); + } + /// /// test手环 http://localhost:7000/mes/andonManagement/interaction/test diff --git a/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs b/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs index 553b677..ad39739 100644 --- a/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs +++ b/DOAN.Service/JobKanban/IService/IWorkorderProgressService.cs @@ -52,5 +52,7 @@ namespace DOAN.Service.JobKanban.IService EchartsOptions GetHourlyProduction(string groupCode); + + int GetLastHourCompletionRate(string workorder); } } diff --git a/DOAN.Service/JobKanban/WorkorderProgressService.cs b/DOAN.Service/JobKanban/WorkorderProgressService.cs index b68bb53..05815c6 100644 --- a/DOAN.Service/JobKanban/WorkorderProgressService.cs +++ b/DOAN.Service/JobKanban/WorkorderProgressService.cs @@ -1,6 +1,8 @@ -using DOAN.Infrastructure.Helper; +using DOAN.Common.SocketHelper; +using DOAN.Infrastructure.Helper; using DOAN.Model.JobKanban; using DOAN.Model.mes.echarts; +using DOAN.Model.MES.andon; using DOAN.Model.MES.base_; using DOAN.Model.MES.group; using DOAN.Model.MES.mm; @@ -11,6 +13,7 @@ using DOAN.Model.Mobile; using DOAN.Service.JobKanban.IService; using DOAN.Service.MES.mm.line; using DOAN.Service.MES.product; +using DOAN.ServiceCore.MyMatchPush; using Infrastructure.Attribute; using Mapster; using NPOI.SS.Formula.Functions; @@ -27,6 +30,13 @@ namespace DOAN.Service.JobKanban; public class WorkorderProgressService : BaseService, IWorkorderProgressService { private ProProducttypeSpecService _proProducttypeSpecService = new ProProducttypeSpecService(); + + private readonly SocketGatewayServer _socketGateway; + public WorkorderProgressService(SocketGatewayServer socketGateway) + { + _socketGateway = socketGateway; + } + public List GetRoutes() { return Context.Queryable().Where(it => it.Status == 1).ToList(); @@ -76,7 +86,7 @@ public class WorkorderProgressService : BaseService, IWorkorderPro .Where(it => it.WorkorderDate == today) .Where(it => it.LineCode == line_code) .Where(it => it.GroupCode == group_code) - .Where(it => it.Status == 1 || it.Status == 2) + .Where(it => it.Status == 1 || it.Status == 2 || it.Status == 4) ; @@ -125,7 +135,7 @@ public class WorkorderProgressService : BaseService, IWorkorderPro .Where(it => it.WorkorderDate == today) .Where(it => it.LineCode == line_code) .Where(it => it.GroupCode == group_code) - .Where(it => it.Status == 1 || it.Status == 2) + .Where(it => it.Status == 1 || it.Status == 2 || it.Status == 4) .Count(); return kanbanInfo; @@ -499,6 +509,11 @@ public class WorkorderProgressService : BaseService, IWorkorderPro }*/ + // 当前工单上一个小时的完成率低于90%,给出提示信息; + + + + return result; } @@ -590,47 +605,42 @@ public class WorkorderProgressService : BaseService, IWorkorderPro public EchartsOptions GetHourlyProduction(string groupCode) { - EchartsOptions echartsOptions = new EchartsOptions(); - echartsOptions.Title = new EchartsTitle("今日各组实时完工数与计划完工柱状图", "获取今日各组实时每小时完工数柱状图"); - //横轴 8:00 20:00 每60分钟累加一次 - // 1.获取这个班组工作时间 + // 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; + TimeSpan startTimeSpan = new TimeSpan(Shifts.StartTime.Value.Hour, Shifts.StartTime.Value.Minute, Shifts.StartTime.Value.Second); + DateTime todayAtStart = today + startTimeSpan; + DateTime[] dateTimeArray = new DateTime[intervals]; for (int i = 0; i < intervals; i++) { - TimeSpan spanItem = new TimeSpan(0, 60 * i, 0);// 60分钟间隔 - DateTime TodayItem = todayAtEight + spanItem; + TimeSpan spanItem = new TimeSpan(0, 60 * i, 0); + DateTime TodayItem = todayAtStart + 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(...) 将处理后的时间戳转换回日期时间格式,以便在结果集中显示。 + + // 2. 获取实际完成数(保持不变) + EchartsSeries actualSeries = new EchartsSeries(); + actualSeries.Name = groupCode + "组今日实时完成数(每60分钟)"; + actualSeries.Type = "bar"; + List actualSeriesDatas = new List(); string year = DateTime.Today.Year.ToString("D4"); string month = DateTime.Today.Month.ToString("D2"); @@ -639,100 +649,143 @@ public class WorkorderProgressService : BaseService, IWorkorderPro 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; + " GROUP BY FLOOR( UNIX_TIMESTAMP( d.created_time ) / 3600 ) ORDER BY time_period"; // 修正:应该是3600而不是600 + DataTable result = Context.Ado.GetDataTable(sql, new { groupCode = groupCode }); 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() + EchartsSeriesData seriesData = new EchartsSeriesData() { Name = value.ToString("HH:mm"), Value = count }; - echartsSeriesDatas.Add(echartsSeriesData); + actualSeriesDatas.Add(seriesData); } - 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++; + // 补全缺失的时间点数据为0 + CompleteMissingTimePoints(ref actualSeriesDatas, XAxis.Data.ToList()); + actualSeries.Data = actualSeriesDatas.OrderBy(it => it.Name).ToList(); + echartsOptions.Series.Add(actualSeries); - } - - 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(); + // 3. 修正计划完成数计算 + List ProWorkorderList = Context.Queryable() + .Where(it => it.GroupCode == groupCode && it.WorkorderDate == DateTime.Today).ToList(); if (ProWorkorderList.Count() == 0) { return null; } - //求出总产量需要多少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)); + // 计算总产量 + int totalProduct = ProWorkorderList.Sum(it => it.DeliveryNum ?? 0); + + // 计算总工作时间段数量 + int totalHours = intervals; + + // 计算每个时间段的计划产量(均匀分配) + int plannedProductPerHour = totalProduct / totalHours; + + // 处理不能整除的情况,最后一个时间段加上余数 + int remainder = totalProduct % totalHours; + + EchartsSeries plannedSeries = new EchartsSeries(); + plannedSeries.Name = groupCode + "组今日计划完成数(每60分钟)"; + plannedSeries.Type = "bar"; + List plannedSeriesDatas = new List(); - 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 seriesData = new EchartsSeriesData(); + seriesData.Name = dateTimeArray[i].ToString("HH:mm"); - EchartsSeriesData echartsSeriesData = new EchartsSeriesData(); - echartsSeriesData.Name = dateTimeArray[i].ToString("HH:mm"); - echartsSeriesData.Value = productOfeachTenMin; - echartsSeriesData02s.Add(echartsSeriesData); - //all_plan_product = all_plan_product + productOfeachTenMin; + // 均匀分配计划产量 + int plannedValue = plannedProductPerHour; + if (i == dateTimeArray.Length - 1) + { + plannedValue += remainder; // 最后一个时间段加上余数 + } + + seriesData.Value = plannedValue; + plannedSeriesDatas.Add(seriesData); } - echartsSeries02.Data = echartsSeriesData02s; - echartsOptions.Series.Add(echartsSeries02); + + plannedSeries.Data = plannedSeriesDatas; + echartsOptions.Series.Add(plannedSeries); return echartsOptions; + } + + // 辅助方法:补全缺失的时间点 + private void CompleteMissingTimePoints(ref List seriesDatas, List allTimePoints) + { + var existingTimePoints = seriesDatas.Select(s => s.Name).ToHashSet(); + + foreach (string timePoint in allTimePoints) + { + if (!existingTimePoints.Contains(timePoint)) + { + seriesDatas.Add(new EchartsSeriesData() + { + Name = timePoint, + Value = 0 // 缺失的时间点设为0 + }); + } + } + } + + + + public int GetLastHourCompletionRate(string workorder) + { + var workorderInfo = Context.Queryable().Where(it => it.Workorder == workorder) + .Select(it => new { it.Workorder, it.Beat ,it.LineCode}).First(); + if (workorderInfo == null) + { + return 0; + } + DateTime oneHourAgo = DateTime.Now.AddHours(-1); + int finishedCount = Context.Queryable().SplitTable(tabs => tabs.Take(1)) + .Where(it => it.CreatedTime >= oneHourAgo) + .Where(it => it.Workorder == workorder) + .Count(); + int totalCount =workorderInfo.Beat??0*60*60; + if (totalCount == 0) + { + return 0; + } + float completionRate = finishedCount / totalCount; + if (completionRate <= 0.9f) + { + + // 触发andon + //触发报警逻辑 + AndonFaultRecord record = new AndonFaultRecord(); + record.LineCode = workorderInfo.LineCode; + record.FaultDict = "生产异常报警"; + record.FaultContext = $"工单号:{workorderInfo.Workorder},当前工单上一个小时的完成率低于90%,为{completionRate:F1}%"; + record.AskPerson = "system"; + record.CreatedTime = DateTime.Now; + //发送报警信息 + string message = $"产线:{record.LineCode},\n故障类型:{record.FaultDict},\n故障内容:{record.FaultContext},\n报警人:{record.AskPerson}"; + //发送手表 + Watchup.StartPush(message, _socketGateway); + + record.Id = SnowFlakeSingle.Instance.NextId().ToString(); + record.StartTime = DateTime.Now; + record.Status = 1; + record.CreatedBy = "system"; + record.CreatedTime = DateTime.Now; + int result = Context.Insertable(record).ExecuteCommand(); + + return 1; + } + else + { + return 0; + } } } \ No newline at end of file diff --git a/DOAN.Service/MES/andon/AndonInteractionService.cs b/DOAN.Service/MES/andon/AndonInteractionService.cs index fc0be9d..b8631ed 100644 --- a/DOAN.Service/MES/andon/AndonInteractionService.cs +++ b/DOAN.Service/MES/andon/AndonInteractionService.cs @@ -70,6 +70,29 @@ namespace DOAN.Service.MES.andon // return 0; } + public string CallHandleSCREEN(AndonFaultRecord record) + { + //发送报警信息 + string message = $"产线:{record.LineCode},\n故障类型:{record.FaultDict},\n故障内容:{record.FaultContext},\n报警人:{record.AskPerson}"; + //发送手表 + Watchup.StartPush(message, _socketGateway); + + record.Id = SnowFlakeSingle.Instance.NextId().ToString(); + record.StartTime = DateTime.Now; + record.Status = 1; + record.CreatedBy = "system"; + record.CreatedTime = DateTime.Now; + int result= Context.Insertable(record).ExecuteCommand(); + + if(result>0) + { + return record.Id; + } + return ""; + + } + + /// /// test手表 /// diff --git a/DOAN.Service/MES/andon/IService/IAndonInteractionService.cs b/DOAN.Service/MES/andon/IService/IAndonInteractionService.cs index c8fd0e0..079e78b 100644 --- a/DOAN.Service/MES/andon/IService/IAndonInteractionService.cs +++ b/DOAN.Service/MES/andon/IService/IAndonInteractionService.cs @@ -15,6 +15,10 @@ namespace DOAN.Service.MES.andon.IService int TestWatch(); int CallHandle(AndonFaultRecord query); + string CallHandleSCREEN(AndonFaultRecord query); + + + int SignIn(AndonFaultRecord response); List WaitingResponse(); /// diff --git a/DOAN.Service/MES/quality/FQC/QcFinishedproductDefectService.cs b/DOAN.Service/MES/quality/FQC/QcFinishedproductDefectService.cs index e5e5eb2..eb10b03 100644 --- a/DOAN.Service/MES/quality/FQC/QcFinishedproductDefectService.cs +++ b/DOAN.Service/MES/quality/FQC/QcFinishedproductDefectService.cs @@ -1,9 +1,13 @@ -using DOAN.Model; +using DOAN.Common.SocketHelper; +using DOAN.Model; +using DOAN.Model.MES.andon; +using DOAN.Model.MES.product; using DOAN.Model.MES.quality.FQC; using DOAN.Model.MES.quality.IQC; using DOAN.Model.MES.quality.IQC.Dto; using DOAN.Repository; using DOAN.Service.MES.quality.FQC.IService; +using DOAN.ServiceCore.MyMatchPush; using Infrastructure.Attribute; using static ICSharpCode.SharpZipLib.Zip.ExtendedUnixData; @@ -15,6 +19,11 @@ namespace DOAN.Service.MES.quality.FQC [AppService(ServiceType = typeof(IQcFinishedproductDefectService), ServiceLifetime = LifeTime.Transient)] public class QcFinishedproductDefectService : BaseService, IQcFinishedproductDefectService { + private readonly SocketGatewayServer _socketGateway; + public QcFinishedproductDefectService(SocketGatewayServer socketGateway) + { + _socketGateway = socketGateway; + } public bool AddDefectNum(string WorkOrder, string DefectCode) { int flag = 0; @@ -24,6 +33,7 @@ namespace DOAN.Service.MES.quality.FQC .Where(it => it.Workorder == WorkOrder) .Where(it => it.DefectCode == DefectCode) .First(); + ProWorkorder proWorkorder= Context.Queryable().Where(it => it.Workorder == WorkOrder).First(); if (existingRecord != null) { @@ -37,6 +47,37 @@ namespace DOAN.Service.MES.quality.FQC .Where(it => it.Workorder == WorkOrder) .Where(it => it.DefectCode == DefectCode) .ExecuteCommand(); + + //两个小时内达到每5次就报警一次 + bool isExist= Context.Queryable() + .Where(it => it.Workorder == WorkOrder) + .Where(it => it.DefectCode == DefectCode) + .Where(it=>it.UpdatedTime-it.CreatedTime>=TimeSpan.FromHours(2)) + .Where(it => it.Number % 5 == 0&&it.Number>=5) + .Any(); + if(isExist) + { + //触发报警逻辑 + AndonFaultRecord record=new AndonFaultRecord(); + record.LineCode = proWorkorder.LineCode; + record.FaultDict = "成品缺陷报警"; + record.FaultContext = $"工单号:{WorkOrder},缺陷代码:{DefectCode},缺陷次数:{existingRecord.Number + 1}"; + record.AskPerson = "system"; + record.CreatedTime= DateTime.Now; + //发送报警信息 + string message = $"产线:{record.LineCode},\n故障类型:{record.FaultDict},\n故障内容:{record.FaultContext},\n报警人:{record.AskPerson}"; + //发送手表 + Watchup.StartPush(message, _socketGateway); + + record.Id = SnowFlakeSingle.Instance.NextId().ToString(); + record.StartTime = DateTime.Now; + record.Status = 1; + record.CreatedBy = "system"; + record.CreatedTime = DateTime.Now; + int result = Context.Insertable(record).ExecuteCommand(); + + } + } else { diff --git a/global.json b/global.json new file mode 100644 index 0000000..285b9d3 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.416", + "rollForward": "latestFeature" + } +}