361 lines
15 KiB
C#
361 lines
15 KiB
C#
using DataLinkMesWork1;
|
||
using MesServerWork;
|
||
using MisDataFunDll;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.Data.SqlClient;
|
||
|
||
namespace MesServerFunctions
|
||
{
|
||
partial class FunctionMES
|
||
{
|
||
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";
|
||
private static readonly object ManualStaticMinutesLocker = new object();
|
||
private static readonly Dictionary<string, decimal> ManualStaticMinutesByEngine = new Dictionary<string, decimal>();
|
||
|
||
private class ManualStaticOutProduct
|
||
{
|
||
public string EngineID { get; set; }
|
||
public string OrderForm { get; set; }
|
||
public string EngineType { get; set; }
|
||
public int EngineTypeID { get; set; }
|
||
}
|
||
|
||
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; }
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
private bool TryGetManualStaticStartTime(string engineID, out string sourceOpName, out DateTime startTime, out string message)
|
||
{
|
||
sourceOpName = "";
|
||
startTime = DateTime.MinValue;
|
||
message = "";
|
||
|
||
try
|
||
{
|
||
// 取 OP400/OP410/OP420 最后一条有效离开记录作为常温静置开始时间。
|
||
string procedureName = "OP431手动静置出库_前序离开时间_查询";
|
||
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 = "未查询到 OP400/OP410/OP420 前序出站记录";
|
||
return false;
|
||
}
|
||
|
||
sourceOpName = Convert.ToString(dt.Rows[0]["工位号"]);
|
||
startTime = Convert.ToDateTime(dt.Rows[0]["开始静置时间"]);
|
||
return true;
|
||
}
|
||
catch (Exception err)
|
||
{
|
||
message = "查询前序出站时间失败:" + err.Message;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
private int NormalizeManualStaticQualityMask(int qualityMask)
|
||
{
|
||
return qualityMask == 1 ? 1 : 2;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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);
|
||
|
||
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;
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
private void SendManualStaticPadMqttMessage(string command, string opName, string value)
|
||
{
|
||
SendMessage.AsyncSendNotOpMessage(ManualStaticPadTopic, "MES|" + command + "|" + opName + "|" + value);
|
||
}
|
||
|
||
private void RememberManualStaticMinutesForProduct(string engineID, decimal minStaticMinutes)
|
||
{
|
||
lock (ManualStaticMinutesLocker)
|
||
{
|
||
ManualStaticMinutesByEngine[engineID] = minStaticMinutes;
|
||
}
|
||
}
|
||
|
||
private bool TryGetManualStaticMinutesForProduct(string engineID, out decimal minStaticMinutes)
|
||
{
|
||
lock (ManualStaticMinutesLocker)
|
||
{
|
||
if (ManualStaticMinutesByEngine.ContainsKey(engineID))
|
||
{
|
||
minStaticMinutes = ManualStaticMinutesByEngine[engineID];
|
||
return true;
|
||
}
|
||
}
|
||
|
||
minStaticMinutes = DefaultManualStaticMinutes;
|
||
return false;
|
||
}
|
||
}
|
||
}
|