Files
52-NingdePDC-MesProject/MisDataFunction/Function1062/DataAccess/DataAccess.cs
2026-06-05 13:52:52 +08:00

436 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using DataLinkMesWork1;
using MesServerWork;
using MisDataFunDll;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
namespace MesServerFunctions
{
partial class FunctionMES
{
// OP431 手动静置出库固定配置。
private const string ManualStaticOutOpName = "OP431";
private const string ManualStaticPadTopic = "OP431PAD";
private const decimal DefaultManualStaticMinutes = 30m;
private const string ManualStaticLocation = "777777";
private const string ManualStaticOperator = "PAD";
// 记录 PAD 确认时使用的最小静置分钟数,保存质量数据时沿用同一标准。
private static readonly object ManualStaticMinutesLocker = new object();
private static readonly Dictionary<string, decimal> ManualStaticMinutesByEngine = new Dictionary<string, decimal>();
/// <summary>
/// OP431 当前产品基础信息。
/// </summary>
private class ManualStaticOutProduct
{
public string EngineID { get; set; }
public string OrderForm { get; set; }
public string EngineType { get; set; }
public int EngineTypeID { get; set; }
}
/// <summary>
/// OP431 静置质量数据计算结果。
/// </summary>
private class ManualStaticOutData
{
public string SourceOpName { get; set; }
public DateTime StartTime { get; set; }
public DateTime FinishTime { get; set; }
public decimal Minutes { get; set; }
public int QualityMask { get; set; }
}
/// <summary>
/// 查询产品订单、型号和机型代码。
/// </summary>
private bool TryGetManualStaticOutProduct(string engineID, out ManualStaticOutProduct product, out string message)
{
product = null;
message = "";
if (string.IsNullOrWhiteSpace(engineID))
{
message = "产品码为空";
return false;
}
MisDataFun.Moby_Select_PDC(engineID, out string orderForm, out string engineType, out int engineTypeID);
if (engineTypeID <= 0 || string.IsNullOrWhiteSpace(engineType))
{
message = "未查询到产品执行信息:" + engineID;
return false;
}
product = new ManualStaticOutProduct
{
EngineID = engineID.Trim(),
OrderForm = orderForm,
EngineType = engineType,
EngineTypeID = engineTypeID
};
return true;
}
/// <summary>
/// 查询 OP420 记录的线下手动静置开始时间。
/// </summary>
private bool TryGetManualStaticStartTime(string engineID, out string sourceOpName, out DateTime startTime, out string message)
{
sourceOpName = "";
startTime = DateTime.MinValue;
message = "";
try
{
// OP431 手动出库以 OP420 记录的线下静置开始时间为准。
string procedureName = "线下手动静置_记录_查询";
SqlParameter[] sqlParameters = new SqlParameter[1];
sqlParameters[0] = new SqlParameter("@产品编号", engineID);
DataAccess.ExecuteStoredProcedure(procedureName, ref sqlParameters, out DataTable dt, out string errorMessage);
if (!string.IsNullOrWhiteSpace(errorMessage))
{
message = "查询线下手动静置开始时间失败:" + errorMessage;
return false;
}
if (dt == null || dt.Rows.Count == 0)
{
message = "未查询到线下手动静置开始记录:" + engineID;
return false;
}
sourceOpName = "OP420";
startTime = Convert.ToDateTime(dt.Rows[0]["静置开始时间"]);
return true;
}
catch (Exception err)
{
message = "查询线下手动静置开始时间失败:" + err.Message;
return false;
}
}
/// <summary>
/// OP431 保存时回写线下手动静置结束时间和静置时长。
/// </summary>
private bool FinishManualStaticRecord(string engineID, DateTime finishTime, decimal minutes, out string message)
{
message = "";
SqlParameter[] sqlParameters = new SqlParameter[]
{
new SqlParameter("@产品编号", engineID),
new SqlParameter("@静置结束时间", finishTime),
new SqlParameter("@静置时长", minutes)
};
DataAccess.ExecuteStoredProcedure("线下手动静置_记录_结束", ref sqlParameters, out string errorMessage);
if (!string.IsNullOrWhiteSpace(errorMessage))
{
message = "回写线下手动静置结束时间失败:" + errorMessage;
return false;
}
return true;
}
/// <summary>
/// 生成 OP431 保存时需要的静置时间和质量标识。
/// </summary>
private bool TryBuildManualStaticOutData(string engineID, int qualityMask, out ManualStaticOutData staticOutData, out string message)
{
staticOutData = null;
message = "";
if (!TryGetManualStaticStartTime(engineID, out string sourceOpName, out DateTime startTime, out message))
{
return false;
}
// PLC 允许保存到达时的服务器时间作为静置完成时间。
DateTime finishTime = DateTime.Now;
decimal minutes = Math.Round(Convert.ToDecimal((finishTime - startTime).TotalMinutes), 2);
staticOutData = new ManualStaticOutData
{
SourceOpName = sourceOpName,
StartTime = startTime,
FinishTime = finishTime,
Minutes = minutes,
QualityMask = NormalizeManualStaticQualityMask(qualityMask)
};
return true;
}
/// <summary>
/// 标准化质量标识1 为合格,其余均按不合格处理。
/// </summary>
private int NormalizeManualStaticQualityMask(int qualityMask)
{
return qualityMask == 1 ? 1 : 2;
}
/// <summary>
/// 判断静置时长是否达到 PAD 配置的最小分钟数。
/// </summary>
private bool IsManualStaticTimeEnough(ManualStaticOutData staticOutData, decimal minStaticMinutes, out string message)
{
message = "";
if (staticOutData.Minutes < minStaticMinutes)
{
message = "静置时间不足 " + minStaticMinutes.ToString("0.##") + " 分钟,当前已静置 " + staticOutData.Minutes + " 分钟";
return false;
}
return true;
}
/// <summary>
/// 直接写入 OP431 四项静置质量数据。
/// </summary>
private bool WriteManualStaticOutQualityData(string opName, string engineID, ManualStaticOutData data, out string message)
{
message = "";
int idTagCf = 0;
if (!TryGetManualStaticMinutesForProduct(engineID, out decimal rememberedMinStaticMinutes))
{
message = "未找到PAD确认的最小静置标准请在PAD重新确认上线后再保存" + engineID;
return false;
}
// 先写质量索引,取返回的 Id_tagCF 作为明细关联键。
string minStaticMinutes = rememberedMinStaticMinutes.ToString("0.##");
// OP431 的静置数据由 Function 直接落库,不再通过 OnlineCheckData 做 HTTP 自调用。
SqlParameter[] indexParams = new SqlParameter[6];
indexParams[0] = new SqlParameter("@EngineID", engineID);
indexParams[1] = new SqlParameter("@工位号", opName);
indexParams[2] = new SqlParameter("@操作者工号", ManualStaticOperator);
indexParams[3] = new SqlParameter("@扫码时间", data.FinishTime);
indexParams[4] = new SqlParameter("@合格标志", data.QualityMask);
indexParams[5] = new SqlParameter("@Id_tagCF", idTagCf);
indexParams[5].Direction = ParameterDirection.Output;
DataAccess.ExecuteStoredProcedure("测量数据合格索引_增加_测试设备", ref indexParams, out string errorMessage);
if (!string.IsNullOrWhiteSpace(errorMessage))
{
message = "写入静置质量索引失败:" + errorMessage;
return false;
}
idTagCf = Convert.ToInt32(indexParams[5].Value.ToString());
if (idTagCf <= 0)
{
message = "写入静置质量索引失败:未生成 Id_tagCF";
return false;
}
// 四条质量明细必须与 MOM 数据收集组配置的 TagID 保持一致。
object[,] detailRows = new object[,]
{
{ "OP431_M0001_001", "开始静置时间", "min", "1", "60", minStaticMinutes, data.StartTime.ToString("yyyy-MM-dd HH:mm:ss"), data.QualityMask },
{ "OP431_M0001_002", "静置完成时间", "min", "1", "60", minStaticMinutes, data.FinishTime.ToString("yyyy-MM-dd HH:mm:ss"), data.QualityMask },
{ "OP431_M0001_003", "静置时间差", "min", "1", "60", minStaticMinutes, data.Minutes.ToString("0.##"), data.QualityMask },
{ "OP431_M0001_004", "静置库位号", "静置库位", "-1", "-1", "-1", ManualStaticLocation, data.QualityMask }
};
for (int i = 0; i < detailRows.GetLength(0); i++)
{
SqlParameter[] detailParams = new SqlParameter[]
{
new SqlParameter("@Id_tagCF", idTagCf),
new SqlParameter("@EngineID", engineID),
new SqlParameter("@TagID", detailRows[i, 0]),
new SqlParameter("@TagValue", detailRows[i, 6]),
new SqlParameter("@IsGoodBad", detailRows[i, 7]),
new SqlParameter("@工位号", opName),
new SqlParameter("@理论值", detailRows[i, 3]),
new SqlParameter("@上限值", detailRows[i, 4]),
new SqlParameter("@下限值", detailRows[i, 5]),
new SqlParameter("@测量项目", detailRows[i, 1]),
new SqlParameter("@测量单位", detailRows[i, 2])
};
DataAccess.ExecuteStoredProcedure("测量数据合格_增加_测试设备", ref detailParams, out errorMessage);
if (!string.IsNullOrWhiteSpace(errorMessage))
{
message = "写入静置质量明细失败:" + errorMessage;
return false;
}
}
return true;
}
/// <summary>
/// 确认上线前校验托盘到位和 PLC 申请上线状态。
/// </summary>
private bool CheckManualStaticPadPrecondition(string opName, out string message)
{
message = "";
MIS_BASE.Read_WorkStart(opName, out bool workStart);
if (!workStart)
{
message = "当前无托盘到位信号,不允许扫码确认";
return false;
}
MIS_BASE.Read_PartLoad(opName, out bool partLoad);
if (!partLoad)
{
message = "PLC尚未申请上线不允许扫码确认";
return false;
}
return true;
}
/// <summary>
/// PAD 确认上线:校验静置条件,通过后写入 PLC 产品区。
/// </summary>
private bool ConfirmManualStaticOutProduct(string opName, string engineID, decimal minStaticMinutes, out string message)
{
message = "";
// PAD 确认上线时必须重新校验托盘和 PLC 申请状态,不能复用查询时的旧结果。
if (!CheckManualStaticPadPrecondition(opName, out message))
{
return false;
}
if (!TryGetManualStaticOutProduct(engineID, out ManualStaticOutProduct product, out message))
{
return false;
}
if (!TryBuildManualStaticOutData(product.EngineID, 1, out ManualStaticOutData staticOutData, out message))
{
return false;
}
// 二次校验静置时间,最小分钟数来自 PAD 当前配置,默认 30 分钟。
if (!IsManualStaticTimeEnough(staticOutData, minStaticMinutes, out message))
{
return false;
}
MIS_BASE.Read_PalletCode(opName, out int palletCode);
MisDataFun.OpName_MIS_MOBY_Insert(opName, product.EngineTypeID, product.EngineType, product.EngineID, palletCode, product.OrderForm, 0, 0, "");
// 写入 MES->PLC 产品区PLC 收到 EngineTypeGetOver_MES 后再回传 44 做进站校验。
MIS_BASE.Write_EngineTypeID_MES(opName, product.EngineTypeID);
MIS_BASE.Write_EngineID_MES(opName, product.EngineID);
MIS_BASE.Write_EngineType_MES(opName, product.EngineType);
MIS_BASE.Write_OrderForm_MES(opName, product.OrderForm);
MIS_BASE.Write_EngineTypeGetOver_MES(opName, true);
RememberManualStaticMinutesForProduct(product.EngineID, minStaticMinutes);
message = "PAD确认上线成功" + product.EngineID;
return true;
}
/// <summary>
/// PAD 扫码查询:返回产品、静置时间和是否允许确认。
/// </summary>
private bool QueryManualStaticOutProduct(string opName, string engineID, decimal minStaticMinutes, out string resultMessage)
{
resultMessage = "";
// 扫码查询只返回产品与静置信息,不写 PLC、不调用进站也不依赖托盘到位/PLC申请上线实时消息。
// PAD 页面可能不是一直打开,托盘到位等实时 MQTT 可能丢失,最终状态由确认上线时后端再次校验。
if (!TryGetManualStaticOutProduct(engineID, out ManualStaticOutProduct product, out resultMessage))
{
return false;
}
if (!TryBuildManualStaticOutData(product.EngineID, 1, out ManualStaticOutData staticOutData, out resultMessage))
{
return false;
}
bool timeEnough = IsManualStaticTimeEnough(staticOutData, minStaticMinutes, out string timeMessage);
MIS_BASE.Read_PartLoad(opName, out bool partLoad);
// PAD 直接使用 canConfirm 控制按钮,不再自行组合判断条件。
bool canConfirm = timeEnough && partLoad;
string confirmMessage = canConfirm
? "允许确认上线"
: !timeEnough
? timeMessage
: "PLC尚未申请上线不允许确认上线";
resultMessage = string.Join("^", new string[]
{
product.EngineID,
product.OrderForm,
product.EngineType,
product.EngineTypeID.ToString(),
staticOutData.SourceOpName,
staticOutData.StartTime.ToString("yyyy-MM-dd HH:mm:ss"),
staticOutData.Minutes.ToString("0.##"),
minStaticMinutes.ToString("0.##"),
partLoad ? "1" : "0",
canConfirm ? "1" : "0",
confirmMessage
});
return true;
}
/// <summary>
/// 将 PAD 业务结果同时发送到 PAD 主题和工位主题。
/// </summary>
private void SendManualStaticOutResult(string opName, string command, bool success, string message)
{
string value = (success ? "1" : "0") + "^" + message;
SendManualStaticPadMqttMessage(command, opName, value);
// 同步给工位主题保留现有页面状态监听能力PAD 以 OP431PAD 主题为准。
SendMessageFun.SendInfomation(command, value, opName);
if (!success)
{
SendMessage.AsyncSendMessage_Notice(opName, "【" + opName + "】" + message);
}
}
/// <summary>
/// 向 OP431 PAD 专用主题发送 MQTT 回包。
/// </summary>
private void SendManualStaticPadMqttMessage(string command, string opName, string value)
{
SendMessage.AsyncSendNotOpMessage(ManualStaticPadTopic, "MES|" + command + "|" + opName + "|" + value);
}
/// <summary>
/// 记录产品确认上线时使用的最小静置标准。
/// </summary>
private void RememberManualStaticMinutesForProduct(string engineID, decimal minStaticMinutes)
{
lock (ManualStaticMinutesLocker)
{
ManualStaticMinutesByEngine[engineID] = minStaticMinutes;
}
}
/// <summary>
/// 获取产品确认上线时记录的最小静置标准。
/// </summary>
private bool TryGetManualStaticMinutesForProduct(string engineID, out decimal minStaticMinutes)
{
lock (ManualStaticMinutesLocker)
{
if (ManualStaticMinutesByEngine.ContainsKey(engineID))
{
minStaticMinutes = ManualStaticMinutesByEngine[engineID];
return true;
}
}
minStaticMinutes = DefaultManualStaticMinutes;
return false;
}
}
}