This commit is contained in:
chenlin 2026-01-30 14:21:46 +08:00
commit 8e3b1a15a5
8 changed files with 267 additions and 98 deletions

View File

@ -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 防错并且报工
/// <summary>
/// 防错并且报工
/// 防错并且报工
/// </summary>
/// <param name="workorder"></param>
/// <param name="labelContext"></param>
@ -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%,给出提示信息;
/// <summary>
/// 结果 1 是 触发andon 0 是不触发andon
/// </summary>
/// <param name="workorder"></param>
/// <returns></returns>
/// <exception cref="CustomException"></exception>
[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);
}
}

View File

@ -56,6 +56,29 @@ namespace DOAN.WebApi.Controllers.MES.andon
return SUCCESS(response);
}
/// <summary>
/// 呼叫请求 SCREEN
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
[HttpPost("Call_SCREEN")]
public IActionResult CallHandleSCREEN([FromBody] AndonAskQueryDto query)
{
if (query == null)
{
return SUCCESS(null);
}
AndonFaultRecord record = query.Adapt<AndonFaultRecord>();
record.ToCreate(HttpContext);
var response = _andonInteractionService.CallHandleSCREEN(record);
//TODO 通知到PDA 发消息
_hubContext.Clients.All.SendAsync("Call", query);
return SUCCESS(response);
}
/// <summary>
/// test手环 http://localhost:7000/mes/andonManagement/interaction/test

View File

@ -52,5 +52,7 @@ namespace DOAN.Service.JobKanban.IService
EchartsOptions GetHourlyProduction(string groupCode);
int GetLastHourCompletionRate(string workorder);
}
}

View File

@ -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<ProWorkorder>, IWorkorderProgressService
{
private ProProducttypeSpecService _proProducttypeSpecService = new ProProducttypeSpecService();
private readonly SocketGatewayServer _socketGateway;
public WorkorderProgressService(SocketGatewayServer socketGateway)
{
_socketGateway = socketGateway;
}
public List<BaseWorkRoute> GetRoutes()
{
return Context.Queryable<BaseWorkRoute>().Where(it => it.Status == 1).ToList();
@ -76,7 +86,7 @@ public class WorkorderProgressService : BaseService<ProWorkorder>, 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<ProWorkorder>, 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<ProWorkorder>, IWorkorderPro
}*/
// 当前工单上一个小时的完成率低于90%,给出提示信息;
return result;
}
@ -590,47 +605,42 @@ public class WorkorderProgressService : BaseService<ProWorkorder>, 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<GroupSchedule>().LeftJoin<GroupShift>((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<EchartsSeriesData> echartsSeriesDatas = new List<EchartsSeriesData>();
//UNIX_TIMESTAMP(timestamp) 把 timestamp 转换为自 Unix 纪元以来的秒数。
//FLOOR(... / 600) 将该时间戳除以60010分钟的秒数然后向下取整到最接近的整数。
//*600 再次乘以600以获得每个10分钟周期开始的时间戳。
//FROM_UNIXTIME(...) 将处理后的时间戳转换回日期时间格式,以便在结果集中显示。
// 2. 获取实际完成数(保持不变)
EchartsSeries actualSeries = new EchartsSeries();
actualSeries.Name = groupCode + "组今日实时完成数每60分钟";
actualSeries.Type = "bar";
List<EchartsSeriesData> actualSeriesDatas = new List<EchartsSeriesData>();
string year = DateTime.Today.Year.ToString("D4");
string month = DateTime.Today.Month.ToString("D2");
@ -639,100 +649,143 @@ public class WorkorderProgressService : BaseService<ProWorkorder>, 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<ProWorkorder> ProWorkorderList = Context.Queryable<ProWorkorder>().Where(it => it.GroupCode == groupCode && it.WorkorderDate == DateTime.Today).ToList();
// 3. 修正计划完成数计算
List<ProWorkorder> ProWorkorderList = Context.Queryable<ProWorkorder>()
.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<EchartsSeriesData> plannedSeriesDatas = new List<EchartsSeriesData>();
EchartsSeries echartsSeries02 = new EchartsSeries();
echartsSeries02.Name = groupCode + "组今日计划完成数每60分钟";
echartsSeries02.Type = "bar";
List<EchartsSeriesData> echartsSeriesData02s = new List<EchartsSeriesData>();
//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<EchartsSeriesData> seriesDatas, List<string> 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<ProWorkorder>().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<ProReportworkDetail>().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;
}
}
}

View File

@ -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 "";
}
/// <summary>
/// test手表
/// </summary>

View File

@ -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<AndonFaultRecord> WaitingResponse();
/// <summary>

View File

@ -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<QcFinishedproductDefectCollection>, 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<ProWorkorder>().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<QcFinishedproductDefectCollection>()
.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
{

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"version": "8.0.416",
"rollForward": "latestFeature"
}
}