首次提交

This commit is contained in:
XingCheng3
2026-05-11 11:09:04 +08:00
commit 79c0f9cd43
260 changed files with 33504 additions and 0 deletions

506
SCADA/Funtion/B_DB_Opera.cs Normal file
View File

@@ -0,0 +1,506 @@
using DC_A95;
using ExternalDataSync;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace MesWork
{
/// <summary>
/// 数据库操作
/// </summary>
public class B_DB_Opera
{
public static void Event_WorkStart(string opName, int tagTypeCodeID, string partType, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_WorkStart";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@PartType", partType),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_WorkEnd(string opName, int tagTypeCodeID, string partType
, string barCode, int qualityMask, int outCount, int duration
, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_WorkEnd";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@PartType", partType),
new SqlParameter("@BarCode", barCode),
new SqlParameter("@QualityMask", qualityMask),
new SqlParameter("@OutCount", outCount),
new SqlParameter("@Duration", duration),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_Signal_Log(string opName, int tagTypeCodeID, string value, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_Signal_Log";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@Value", value),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_Alarm_Start(string opName, int tagTypeCodeID, string alarmText, string alarmLevel, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_Alarm_Start";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@AlarmText", alarmText),
new SqlParameter("@AlarmLevel", alarmLevel),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_Alarm_End(string opName, int tagTypeCodeID, string alarmText, string alarmLevel, string tagID)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_Alarm_End";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@AlarmText", alarmText),
new SqlParameter("@AlarmLevel", alarmLevel),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID)
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_AlarmAndTip_Start(string opName, int tagTypeCodeID, string alarmText, string tagID, int Alarm1_Tip2)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_AlarmAndTip_Start";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@TagTypeCodeID", tagTypeCodeID),
new SqlParameter("@AlarmText", alarmText),
new SqlParameter("@ChangeTime",ChangeTime),
new SqlParameter("@TagID", tagID),
new SqlParameter("@Alarm1_Tip2", Alarm1_Tip2),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void MES_StatusChange(string opName, int StatusCode, string TriggerTime)
{
var procedureName = "设备管理_状态变化新增";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@opName", opName),
new SqlParameter("@StatusCode", StatusCode),
new SqlParameter("@TriggerTime", TriggerTime),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Query_AlarmData(string opName, out int count, out string text)
{
count = 0;
text = "";
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Query_AlarmData";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out DataTable dt, out string errorMessage);
if (dt.Rows.Count > 0)
{
count = Convert.ToInt32(dt.Rows[0]["COUNT"].ToString());
text = dt.Rows[0]["TEXT"].ToString();
}
}
public static void Event_DeviceStatus_UseTime(string opName, string Onhours, string Workhours, string DeviceStatus)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_DeviceStatus_UseTime";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@Onhours", Onhours),
new SqlParameter("@Workhours", Workhours),
new SqlParameter("@DeviceStatus", DeviceStatus),
new SqlParameter("@ChangeTime",ChangeTime),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
public static void Event_DeviceStatus_Change(string opName, string DeviceStatus)
{
var ChangeTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var procedureName = "Event_DeviceStatus_Change";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@OpName", opName),
new SqlParameter("@DeviceStatus", DeviceStatus),
new SqlParameter("@ChangeTime",ChangeTime),
};
SqlOperation.ExecuteStoredProcedure(procedureName, sqlParameter, out string errorMessage);
}
/// <summary>
/// 请求日志记录
/// </summary>
public static void SaveLog_Request(string StationName, Log_Type LogType, string ReqCode, Log_FromAndTo ReqFrom, Log_FromAndTo ReqTo, string ReqStr, out long AID, string ReqStrSafe = "")
{
AID = -1;
var CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
ReqStr = PLC_R.GetString_CleanGarbled(ReqStr);
var param1 = new SqlParameter[] {
new SqlParameter("@工位号", StationName),
new SqlParameter("@日志类型", LogType.ToString()),
new SqlParameter("@TaskID", ReqCode),
new SqlParameter("@请求CMD",""),
new SqlParameter("@请求FROM",ReqFrom.ToString()),
new SqlParameter("@请求TO",ReqTo.ToString()),
new SqlParameter("@请求时间",CreateTime),
new SqlParameter("@请求内容",ReqStr),
new SqlParameter("@请求内容安全",ReqStrSafe)
};
DataLinkMesWork.SQLCommon.ExecuteStoredProcedure("Event_Log_Request", MesWorkForm.ConnectionString, ref param1, out DataTable dt, out string errorMessage);
if (dt.Rows.Count > 0)
{
AID = Convert.ToInt64(dt.Rows[0]["AID"]);
}
}
/// <summary>
/// 响应日志记录
/// </summary>
public static void SaveLog_Response(string result, long AID)
{
try
{
var CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var param4 = new SqlParameter[]
{
new SqlParameter("@AID",AID),
new SqlParameter("@响应时间",CreateTime),
new SqlParameter("@响应内容",JsonConvert.SerializeObject(result))
};
DataLinkMesWork.SQLCommon.ExecuteStoredProcedure("Event_Log_Respond", MesWorkForm.ConnectionString, ref param4, out string errorMessage);
}
catch (Exception err)
{
}
}
// ====================================================================
// 称重业务数据库方法(调用存储过程,统一 result/msg 返回格式)
// ====================================================================
/// <summary>
/// 从工件管理表查询机型参数(加油量/抽油量/密度/残油量上下限)
/// </summary>
public static bool QueryWorkpieceByModel(string modelNo, out decimal addOilQty, out decimal extractOilQty, out decimal density,
out decimal residualOilUpperLimit, out decimal residualOilLowerLimit)
{
addOilQty = 0; extractOilQty = 0; density = 0; residualOilUpperLimit = 0; residualOilLowerLimit = 0;
var sqlParameter = new SqlParameter[] {
new SqlParameter("@机型号", modelNo)
};
SqlOperation.ExecuteStoredProcedure("称重_查询工件参数", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
var row = dt.Rows[0];
decimal.TryParse(row["加油量"]?.ToString(), out addOilQty);
decimal.TryParse(row["抽油量"]?.ToString(), out extractOilQty);
decimal.TryParse(row["密度"]?.ToString(), out density);
decimal.TryParse(row["残油量上限"]?.ToString(), out residualOilUpperLimit);
decimal.TryParse(row["残油量下限"]?.ToString(), out residualOilLowerLimit);
return true;
}
return false;
}
/// <summary>
/// 插入称重记录
/// </summary>
/// <returns>新记录ID失败返回-1</returns>
public static long InsertWeighingRecord(
string stationNo, string weighType, string engineNo, string modelNo,
string orderNo, string palletNo,
decimal inWeight, decimal outWeight,
decimal addOilQty, decimal extractOilQty, decimal density,
decimal residualOilUpperLimit, decimal residualOilLowerLimit,
decimal oilRelease, decimal residualOil,
int qualityFlag, decimal waterContent,
string dataSource, string worker, int isComplete)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@工位号", stationNo),
new SqlParameter("@称重类型", weighType),
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@机型号", modelNo),
new SqlParameter("@工单号", orderNo),
new SqlParameter("@托盘号", palletNo),
new SqlParameter("@进站重量", inWeight),
new SqlParameter("@离站重量", outWeight),
new SqlParameter("@加油量", addOilQty),
new SqlParameter("@抽油量", extractOilQty),
new SqlParameter("@密度", density),
new SqlParameter("@残油量上限", residualOilUpperLimit),
new SqlParameter("@残油量下限", residualOilLowerLimit),
new SqlParameter("@放油量", oilRelease),
new SqlParameter("@残油量", residualOil),
new SqlParameter("@合格标志", qualityFlag),
new SqlParameter("@水含量", waterContent),
new SqlParameter("@数据来源", dataSource),
new SqlParameter("@操作者", worker),
new SqlParameter("@是否完成", isComplete)
};
SqlOperation.ExecuteStoredProcedure("称重记录_增加", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
return Convert.ToInt64(dt.Rows[0]["ID"]);
}
return -1;
}
/// <summary>
/// 查询放油前记录(按发动机号匹配,称重类型='空中'且未完成)
/// </summary>
public static bool QueryWeighingBeforeOil(string engineNo, out long recordId, out decimal preWeight, out decimal addOilQty, out decimal extractOilQty, out decimal density)
{
recordId = 0; preWeight = 0; addOilQty = 0; extractOilQty = 0; density = 0;
var sqlParameter = new SqlParameter[] {
new SqlParameter("@发动机号", engineNo)
};
SqlOperation.ExecuteStoredProcedure("称重记录_查询放油前", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
var row = dt.Rows[0];
recordId = Convert.ToInt64(row["ID"]);
decimal.TryParse(row["进站重量"]?.ToString(), out preWeight);
decimal.TryParse(row["加油量"]?.ToString(), out addOilQty);
decimal.TryParse(row["抽油量"]?.ToString(), out extractOilQty);
decimal.TryParse(row["密度"]?.ToString(), out density);
return true;
}
return false;
}
/// <summary>
/// 查询指定工位最新的称重记录(不过滤完成状态)
/// </summary>
public static bool QueryLatestRecord(string stationNo, string engineNo,
out long recordId, out decimal preWeight,
out decimal addOilQty, out decimal extractOilQty, out decimal density)
{
recordId = 0; preWeight = 0; addOilQty = 0; extractOilQty = 0; density = 0;
var sqlParameter = new SqlParameter[] {
new SqlParameter("@工位号", stationNo),
new SqlParameter("@发动机号", engineNo)
};
SqlOperation.ExecuteStoredProcedure("称重记录_查询最新", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
var row = dt.Rows[0];
recordId = Convert.ToInt64(row["ID"]);
decimal.TryParse(row["进站重量"]?.ToString(), out preWeight);
decimal.TryParse(row["加油量"]?.ToString(), out addOilQty);
decimal.TryParse(row["抽油量"]?.ToString(), out extractOilQty);
decimal.TryParse(row["密度"]?.ToString(), out density);
return true;
}
return false;
}
/// <summary>
/// 更新称重记录为完成状态(保存称重结果)
/// </summary>
public static bool UpdateWeighingComplete(long recordId, string weighType, string stationNo,
decimal weight, decimal oilRelease, decimal residualOil, decimal waterContent, out int qualityFlag, out string msg)
{
qualityFlag = 2;
msg = "";
var sqlParameter = new SqlParameter[] {
new SqlParameter("@ID", recordId),
new SqlParameter("@称重类型", weighType),
new SqlParameter("@工位号", stationNo),
new SqlParameter("@称重重量", weight),
new SqlParameter("@放油量", oilRelease),
new SqlParameter("@残油量", residualOil),
new SqlParameter("@水含量", waterContent)
};
SqlOperation.ExecuteStoredProcedure("称重记录_完成更新", sqlParameter, out DataTable dt, out string err);
if (dt == null || dt.Rows.Count == 0)
{
msg = string.IsNullOrEmpty(err) ? "称重记录完成更新未返回结果" : err;
return false;
}
qualityFlag = Convert.ToInt32(dt.Rows[0]["result"]);
msg = dt.Rows[0]["msg"].ToString();
return true;
}
// ====================================================================
// 过站记录操作方法
// ====================================================================
/// <summary>
/// 新增过站记录case 44 请求工作时调用)
/// </summary>
public static long InsertStationRecord(string engineNo, string modelNo, string orderNo, string palletNo,
string stationNo, string stationName, decimal addOilQty, decimal extractOilQty, decimal density, string worker)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@机型号", modelNo),
new SqlParameter("@工单号", orderNo),
new SqlParameter("@托盘号", palletNo),
new SqlParameter("@工位号", stationNo),
new SqlParameter("@工位名称", stationName),
new SqlParameter("@加油量", addOilQty),
new SqlParameter("@抽油量", extractOilQty),
new SqlParameter("@油密度", density),
new SqlParameter("@操作者", worker)
};
SqlOperation.ExecuteStoredProcedure("过站记录_新增", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
return Convert.ToInt64(dt.Rows[0]["ID"]);
return -1;
}
/// <summary>
/// 更新过站记录为完成case 19 请求保存时调用)
/// </summary>
public static bool UpdateStationComplete(string engineNo, string stationNo, decimal weight)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@工位号", stationNo),
new SqlParameter("@称重重量", weight)
};
SqlOperation.ExecuteStoredProcedure("过站记录_完成", sqlParameter, out DataTable dt, out string err);
return dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1";
}
// ====================================================================
// 称重曲线数据操作方法
// ====================================================================
/// <summary>
/// 创建曲线段记录(工件到位时调用)
/// </summary>
/// <returns>段ID失败返回-1</returns>
public static long Curve_StartSegment(string opName, string engineNo, string modelNo)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@工位号", opName),
new SqlParameter("@发动机号", engineNo),
new SqlParameter("@机型号", modelNo ?? (object)DBNull.Value)
};
SqlOperation.ExecuteStoredProcedure("称重曲线_开始段", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
return Convert.ToInt64(dt.Rows[0]["ID"]);
}
return -1;
}
/// <summary>
/// 批量写入曲线采样点采集线程每攒够N条调用一次
/// </summary>
/// <param name="segmentId">段ID</param>
/// <param name="points">采样点列表</param>
/// <returns>写入条数失败返回0</returns>
public static int Curve_WritePoints(long segmentId, List<CurveSamplePoint> points)
{
if (points == null || points.Count == 0) return 0;
// 用 JSON 序列化避免小数区域格式和字符串拼接转义问题。
var json = JsonConvert.SerializeObject(points.Select(p => new
{
seq = p.SeqNo,
time = p.SampleTime.ToString("yyyy-MM-ddTHH:mm:ss.fff"),
weight = p.Weight
}));
var sqlParameter = new SqlParameter[] {
new SqlParameter("@段ID", segmentId),
new SqlParameter("@采样数据", json)
};
SqlOperation.ExecuteStoredProcedure("称重曲线_批量写入", sqlParameter, out DataTable dt, out string err);
if (dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1")
{
return Convert.ToInt32(dt.Rows[0]["count"]);
}
return 0;
}
/// <summary>
/// 结束曲线段请求保存时调用关联称重记录ID
/// </summary>
public static bool Curve_EndSegment(long segmentId, long weighingRecordId)
{
var sqlParameter = new SqlParameter[] {
new SqlParameter("@段ID", segmentId),
new SqlParameter("@称重记录ID", weighingRecordId > 0 ? (object)weighingRecordId : DBNull.Value)
};
SqlOperation.ExecuteStoredProcedure("称重曲线_结束段", sqlParameter, out DataTable dt, out string err);
return dt != null && dt.Rows.Count > 0 && dt.Rows[0]["result"].ToString() == "1";
}
}
/// <summary>
/// 曲线采样点数据结构
/// </summary>
public class CurveSamplePoint
{
public int SeqNo;
public DateTime SampleTime;
public decimal Weight;
}
}

View File

@@ -0,0 +1,40 @@
using DC_A95;
using OpcBasic;
using OpcData;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using SystemFramework;
namespace MesWork
{
/// <summary>
/// 设备状态的处理
/// </summary>
public partial class MesWorkForm
{
/// <summary>
/// 设备状态的处理
/// </summary>
/// <param name="e"></param>
private void MIS_Device(object e)
{
var eState = (DeviceDriver_BasicData.PlcStateEvetnArgs)e;
var ip = eState.IP.ToString();
var onLine = eState.OnLine;
}
}
}

View File

@@ -0,0 +1,509 @@
using ExternalDataSync;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
using DC_A95;
/// <summary>
///
/// </summary>
namespace MesWork
{
/// <summary>
/// 处理MIS逻辑
/// </summary>
public partial class MesWorkForm
{
/// <summary>
/// PLC信号变化处理入口
/// 当Bool型信号值变化时由框架自动触发
/// </summary>
/// <param name="e"></param>
private void MIS_Funtion(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
if (msgEvent.TagValue == null) return;
var tagID = msgEvent.TagID.ToString();
var opName = msgEvent.OpName.ToString();
var tagTypeCodeID = (int)msgEvent.TagTypeCodeID;
var tagTypeID = (EnumTagTypeID)msgEvent.TagTypeID;
var isFirstValue = msgEvent.IsFirstValue.ToString().ToLower();
var alarmLevel = msgEvent.ShaftID.ToString();
var alarmMsg = msgEvent.ItemName.ToString();
string tagValue;
switch (tagTypeID)
{
case EnumTagTypeID.BOOL:
tagValue = (Convert.ToInt32(msgEvent.TagValue)).ToString();
break;
default:
tagValue = msgEvent.TagValue.ToString();
break;
}
// ── 报警处理编码900~2000──
if (tagTypeCodeID >= 900 & tagTypeCodeID < 2000)
{
if (ValueT(isFirstValue))
{
//不处理第一次变化的值
return;
}
var opNameNew = opName.Replace("_Alarm", "");
if (tagValue == "1")
{
B_DB_Opera.Event_Alarm_Start(opNameNew, tagTypeCodeID, alarmMsg, alarmLevel, tagID);
}
else
{
B_DB_Opera.Event_Alarm_End(opNameNew, tagTypeCodeID, alarmMsg, alarmLevel, tagID);
}
}
// ── 信号分发 ──
switch (tagTypeCodeID)
{
case 2: // 工件到位 → 启动/停止实时重量曲线采集
if (tagValue == "1")
{
WeighingPage?.AppendLog($"[{opName}] 工件到位信号触发");
CurveCollector_Start(opName);
}
else
{
CurveCollector_Stop(opName);
}
break;
case 14: // PLC心跳 → 回写PC心跳
WritePLC_IF(15, opName, tagValue);
break;
case 44: // 请求工作
if (ValueT(isFirstValue)) return;
if (tagValue == "1")
{
WeighingPage?.AppendLog($"[{opName}] PLC请求工作开始处理...");
Weighing_HandleRequestWork(opName, tagID);
}
else
{
WritePLC_IF(66, opName, false); //允许工作
}
break;
case 19: // 请求保存
if (ValueT(isFirstValue)) return;
if (tagValue == "1")
{
CurveCollector_StopSampling(opName, true); // 先停止采样保存完成后再关联称重记录ID
WeighingPage?.AppendLog($"[{opName}] PLC请求保存数据...");
Weighing_HandleRequestSave(opName, tagID);
}
else
{
WritePLC_IF(20, opName, false); //保存完成
WritePLC_IF(21, opName, false); //合格标志
}
break;
case 203: // 设备状态
B_DB_Opera.Event_DeviceStatus_Change(opName, tagValue);
break;
default:
break;
}
}
catch (Exception err)
{ }
}
// ====================================================================
// 称重交互核心方法
// ====================================================================
/// <summary>
/// 获取称重类型:地面/放油前/放油后
/// Ground模式统一返回"地面"
/// Hanging模式OP10=放油前OP20=放油后
/// </summary>
private string GetWeighingType(string opName)
{
if (WeighingType == "Ground")
return "地面";
// Hanging模式
return opName == "OP10" ? "放油前" : "放油后";
}
/// <summary>
/// 处理请求工作信号 (tagTypeCodeID=44)
/// 流程:读取工件信息 → 查询参数 → 写过站记录 → 按类型处理称重记录 → 回写允许工作
/// </summary>
private void Weighing_HandleRequestWork(string opName, string tagID)
{
B_DB_Opera.SaveLog_Request(opName, Log_Type.ZK_MesHandler, "MIS_Function",
Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"触发信号【44-请求工作】", out long AID);
try
{
// 1. 读取PLC工件信息
string engineNo = PLC_R.GetString_CleanGarbled(ReadPLC(10, opName)); // 总成编号
string modelNo = PLC_R.GetString_CleanGarbled(ReadPLC(11, opName)); // 机型号
string orderNo = PLC_R.GetString_CleanGarbled(ReadPLC(39, opName)); // 工单号
string palletNo = PLC_R.GetString_CleanGarbled(ReadPLC(80, opName)); // 托盘号
B_DB_Opera.SaveLog_Response($"【{opName}】读取工件:发动机={engineNo},机型={modelNo},工单={orderNo},托盘={palletNo}", AID);
// 推送工件信息到称重UI
int stIdx = WeighingPage?.GetStationIndex(opName) ?? 1;
WeighingPage?.UpdateStationInfo(stIdx, engineNo, modelNo, orderNo);
WeighingPage?.AppendLog($"[{opName}] 读取工件:{engineNo},机型={modelNo}");
// 2. 查询工件管理参数(加油量/抽油量/密度/残油量上下限)
if (!B_DB_Opera.QueryWorkpieceByModel(modelNo, out decimal addOilQty, out decimal extractOilQty, out decimal density,
out decimal residualOilUpperLimit, out decimal residualOilLowerLimit))
{
WritePLC_IF(112, opName, 3); // 报警代码=3机型错误
B_DB_Opera.SaveLog_Response($"【{opName}】工件管理中未找到机型[{modelNo}]的参数,发动机={engineNo}", AID);
WeighingPage?.AppendLog($"[{opName}] 机型[{modelNo}]未找到配置参数");
return;
}
// 3. 推送工件参数到UI
WeighingPage?.UpdateOilInfo(stIdx, addOilQty, extractOilQty, density, 0, 0);
// 4. 确定称重类型和工位名称
string weighType = GetWeighingType(opName); // 地面/放油前/放油后
string stationName = weighType; // 工位名称直接用称重类型名
// 4. 写过站记录所有类型统一INSERT一条到达时间=NOW
B_DB_Opera.InsertStationRecord(engineNo, modelNo, orderNo, palletNo,
opName, stationName, addOilQty, extractOilQty, density, Curr_UserName);
// 5. 按类型处理称重记录
if (weighType == "放油后")
{
// 空中放油后:查找最新放油前记录(无论是否完成,支持多次测量)
if (!B_DB_Opera.QueryWeighingBeforeOil(engineNo, out _, out decimal preWeight, out _, out _, out _))
{
// 写报警代码=3发动机号错误阻断流程
WritePLC_IF(112, opName, 3);
B_DB_Opera.SaveLog_Response($"【{opName}】未找到发动机[{engineNo}]的空中称重记录", AID);
WeighingPage?.AppendLog($"[{opName}] 未找到[{engineNo}]放油前记录,进站失败");
return;
}
else
{
B_DB_Opera.SaveLog_Response($"【{opName}】空中放油后准备完成,发动机={engineNo},机型={modelNo},放油前重量={preWeight}kg", AID);
}
}
else
{
// 地面 或 空中放油前:每次都新建称重记录(支持同一产品多次测量取最新)
string recordType = weighType == "地面" ? "地面" : "空中";
B_DB_Opera.InsertWeighingRecord(opName, recordType, engineNo, modelNo, orderNo, palletNo,
0, 0, addOilQty, extractOilQty, density, residualOilUpperLimit, residualOilLowerLimit, 0, 0, 0, 0,
opName, Curr_UserName, isComplete: 0);
B_DB_Opera.SaveLog_Response($"【{opName}】{weighType}准备完成,发动机={engineNo},机型={modelNo},残油量范围={residualOilLowerLimit:F4}-{residualOilUpperLimit:F4}L", AID);
}
// 6. 回写允许工作
WritePLC_IF(66, opName, true);
}
catch (Exception err)
{
WritePLC_IF(112, opName, 1); // 报警代码=1获取数据失败
B_DB_Opera.SaveLog_Response($"【{opName}】请求工作处理失败:{err.Message}", AID);
}
}
/// <summary>
/// 处理请求保存信号 (tagTypeCodeID=19)
/// 流程:读取称重数据 → 更新过站记录 → 按类型处理称重记录 → 回写保存完成
/// </summary>
private void Weighing_HandleRequestSave(string opName, string tagID)
{
B_DB_Opera.SaveLog_Request(opName, Log_Type.ZK_MesHandler, "MIS_Function",
Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"触发信号【19-请求保存】", out long AID);
try
{
// 1. 读取PLC数据
decimal weight = Convert.ToDecimal(ReadPLC(100140, opName)); // 重量
decimal waterContent = Convert.ToDecimal(ReadPLC(100180, opName)); // 水含量
string engineNo = PLC_R.GetString_CleanGarbled(ReadPLC(10, opName)); // 产品编号
long curveWeighingRecordId = 0;
// 2. 更新过站记录(离开时间+称重重量)
B_DB_Opera.UpdateStationComplete(engineNo, opName, weight);
// 3. 确定类型并处理
string weighType = GetWeighingType(opName);
decimal oilReleaseQty = 0;
decimal residualOilQty = 0;
int finalQualityFlag = 1;
string qualityMsg = "";
if (weighType == "地面")
{
// 地面:查最新记录 → 放油量=重量/密度 → 标记完成
B_DB_Opera.QueryLatestRecord(opName, engineNo,
out long recordId, out _, out decimal addOil, out decimal extractOil, out decimal dens);
curveWeighingRecordId = recordId;
oilReleaseQty = dens > 0 ? weight / dens : 0;
residualOilQty = addOil - extractOil - oilReleaseQty;
B_DB_Opera.UpdateWeighingComplete(recordId, "地面", opName,
weight, oilReleaseQty, residualOilQty, waterContent, out finalQualityFlag, out qualityMsg);
B_DB_Opera.SaveLog_Response(
$"【{opName}】地面保存完成:发动机={engineNo},重量={weight}kg放油量={oilReleaseQty:F4}L残油量={residualOilQty:F4}L合格标志={finalQualityFlag}{qualityMsg}", AID);
WeighingPage?.UpdateOilResult(WeighingPage?.GetStationIndex(opName) ?? 1, oilReleaseQty, residualOilQty);
WeighingPage?.AppendLog($"[{opName}] 保存完成:{engineNo},重量={weight:F2}kg放油量={oilReleaseQty:F4}L");
}
else if (weighType == "放油前")
{
// 空中放油前:查最新记录 → 仅写入进站重量,不标记完成
B_DB_Opera.QueryLatestRecord(opName, engineNo,
out long recordId, out _, out _, out _, out _);
curveWeighingRecordId = recordId;
B_DB_Opera.UpdateWeighingComplete(recordId, "空中放油前", opName,
weight, 0, 0, waterContent, out finalQualityFlag, out qualityMsg);
B_DB_Opera.SaveLog_Response(
$"【{opName}】空中放油前保存完成:发动机={engineNo},进站重量={weight}kg合格标志={finalQualityFlag}{qualityMsg}", AID);
WeighingPage?.AppendLog($"[{opName}] 放油前保存:{engineNo},重量={weight:F2}kg");
}
else // 放油后
{
// 空中放油后:查最新放油前记录(无论是否完成),计算放油量更新到该记录
if (B_DB_Opera.QueryWeighingBeforeOil(engineNo, out long recordId, out decimal preWeight,
out decimal addOil, out decimal extractOil, out decimal dens))
{
curveWeighingRecordId = recordId;
oilReleaseQty = dens > 0 ? (preWeight - weight) / dens : 0;
residualOilQty = addOil - extractOil - oilReleaseQty;
B_DB_Opera.UpdateWeighingComplete(recordId, "空中放油后", opName,
weight, oilReleaseQty, residualOilQty, waterContent, out finalQualityFlag, out qualityMsg);
B_DB_Opera.SaveLog_Response(
$"【{opName}】空中放油后保存完成:发动机={engineNo},放油前={preWeight}kg放油后={weight}kg放油量={oilReleaseQty:F4}L残油量={residualOilQty:F4}L合格标志={finalQualityFlag}{qualityMsg}", AID);
WeighingPage?.UpdateOilResult(WeighingPage?.GetStationIndex(opName) ?? 1, oilReleaseQty, residualOilQty);
WeighingPage?.AppendLog($"[{opName}] 放油后保存:{engineNo},放油量={oilReleaseQty:F4}L残油量={residualOilQty:F4}L");
}
else
{
// 找不到放油前记录可能直接进OP20创建一条放油后记录并标记完成
curveWeighingRecordId = B_DB_Opera.InsertWeighingRecord(opName, "空中放油后", engineNo, "", "", "",
0, weight, 0, 0, 0, 0, 0, 0, 0, 2, waterContent,
opName, Curr_UserName, isComplete: 1);
finalQualityFlag = 2;
qualityMsg = "未找到放油前记录";
B_DB_Opera.SaveLog_Response(
$"【{opName}】放油后保存(无放油前记录):发动机={engineNo},放油后重量={weight}kg", AID);
WeighingPage?.AppendLog($"[{opName}] ⚠️ 放油后保存:{engineNo}(无放油前记录,已新建)");
}
}
CurveCollector_EndSegment(opName, curveWeighingRecordId);
// TODO: MES接口数据上传接口对接后实现
if (finalQualityFlag == 2 && !string.IsNullOrWhiteSpace(qualityMsg))
{
WeighingPage?.AppendLog($"[{opName}] 判定不合格:{qualityMsg}");
}
// 4. 先回写上位机判定的合格标志,再回写保存完成
WritePLC_IF(21, opName, finalQualityFlag);
WritePLC_IF(20, opName, true);
}
catch (Exception err)
{
CurveCollector_EndSegment(opName, 0);
WritePLC_IF(112, opName, 2); // 报警代码=2保存失败
B_DB_Opera.SaveLog_Response($"【{opName}】保存处理失败:{err.Message}", AID);
WeighingPage?.AppendLog($"[{opName}] ❌ 保存异常:{err.Message}");
}
}
// ====================================================================
// 扫码枪业务处理
// ====================================================================
/// <summary>
/// 扫码枪扫码完成后的业务处理
/// 将码值写入PLC地址102500触发完成信号100016=11秒后复位
/// </summary>
/// <param name="opName">工位号OP10/OP20由COM口绑定决定</param>
/// <param name="barcode">扫到的条码值</param>
public void Barcode_HandleScan(string opName, string barcode)
{
B_DB_Opera.SaveLog_Request(opName, Log_Type.ZK_MesHandler, "MIS_Function",
Log_FromAndTo.ZK, Log_FromAndTo.ZK, $"扫码枪触发【{opName}】码值={barcode}", out long AID);
try
{
// 1. 写入码值到 PLC 地址 102500PLC_PC_扫码枪值
WritePLC_IF(102500, opName, barcode);
// 2. 写入扫码完成信号 100016 = 1PC_PLC_扫码枪扫码完成
WritePLC_IF(100016, opName, true);
// 3. 日志
B_DB_Opera.SaveLog_Response($"【{opName}】扫码完成:码值={barcode}", AID);
WeighingPage?.AppendLog($"[{opName}] 触发扫码 码值:{barcode}");
// 4. 1秒后复位 100016 = 0
System.Threading.Tasks.Task.Delay(1000).ContinueWith(_ =>
{
try { WritePLC_IF(100016, opName, false); }
catch { }
});
}
catch (Exception err)
{
B_DB_Opera.SaveLog_Response($"【{opName}】扫码处理失败:{err.Message}", AID);
WeighingPage?.AppendLog($"[{opName}] ❌ 扫码处理异常:{err.Message}");
}
}
private class CurveCollectorState
{
public string OpName;
public long SegmentId;
public Thread WorkerThread;
public volatile bool IsRunning;
public bool IsSegmentEnded;
public bool IsWaitingSave;
public int SeqNo;
public readonly object BufferLock = new object();
public readonly List<CurveSamplePoint> Buffer = new List<CurveSamplePoint>();
}
private static readonly ConcurrentDictionary<string, CurveCollectorState> _curveCollectors
= new ConcurrentDictionary<string, CurveCollectorState>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 工件到位后启动实时重量曲线采集。
/// </summary>
private void CurveCollector_Start(string opName)
{
try
{
if (_curveCollectors.TryGetValue(opName, out CurveCollectorState oldState) && !oldState.IsSegmentEnded)
{
if (oldState.IsRunning) return;
CurveCollector_EndSegment(opName, 0);
}
string engineNo = PLC_R.GetString_CleanGarbled(ReadPLC(10, opName));
string modelNo = PLC_R.GetString_CleanGarbled(ReadPLC(11, opName));
long segmentId = B_DB_Opera.Curve_StartSegment(opName, engineNo, modelNo);
if (segmentId <= 0)
{
WeighingPage?.AppendLog($"[{opName}] 曲线段创建失败,未启动采集");
return;
}
var state = new CurveCollectorState
{
OpName = opName,
SegmentId = segmentId,
IsRunning = true
};
state.WorkerThread = new Thread(() => CurveCollector_Work(state))
{
IsBackground = true,
Name = $"CurveCollector_{opName}"
};
_curveCollectors[opName] = state;
state.WorkerThread.Start();
WeighingPage?.AppendLog($"[{opName}] 实时重量曲线采集启动段ID={segmentId}");
}
catch (Exception err)
{
WeighingPage?.AppendLog($"[{opName}] 曲线采集启动异常:{err.Message}");
}
}
/// <summary>
/// 采集线程200ms读取一次实时重量满10条批量写库。
/// </summary>
private void CurveCollector_Work(CurveCollectorState state)
{
while (state.IsRunning)
{
try
{
decimal weight = Convert.ToDecimal(ReadPLC(100220, state.OpName));
List<CurveSamplePoint> writePoints = null;
lock (state.BufferLock)
{
state.Buffer.Add(new CurveSamplePoint
{
SeqNo = ++state.SeqNo,
SampleTime = DateTime.Now,
Weight = weight
});
if (state.Buffer.Count >= 10)
{
writePoints = new List<CurveSamplePoint>(state.Buffer);
state.Buffer.Clear();
}
}
if (writePoints != null)
{
B_DB_Opera.Curve_WritePoints(state.SegmentId, writePoints);
}
}
catch (Exception err)
{
WeighingPage?.AppendLog($"[{state.OpName}] 曲线采样异常:{err.Message}");
}
Thread.Sleep(200);
}
CurveCollector_Flush(state);
}
/// <summary>
/// 停止采样线程并刷出剩余点,但暂不关闭曲线段。
/// </summary>
private void CurveCollector_StopSampling(string opName, bool waitingSave = false)
{
if (!_curveCollectors.TryGetValue(opName, out CurveCollectorState state)) return;
if (waitingSave)
{
state.IsWaitingSave = true;
}
state.IsRunning = false;
if (state.WorkerThread != null && state.WorkerThread.IsAlive)
{
state.WorkerThread.Join(2000);
}
CurveCollector_Flush(state);
}
/// <summary>
/// 工件离开时停止采集并关闭曲线段。
/// </summary>
private void CurveCollector_Stop(string opName)
{
if (_curveCollectors.TryGetValue(opName, out CurveCollectorState state) && state.IsWaitingSave)
{
return;
}
CurveCollector_EndSegment(opName, 0);
}
/// <summary>
/// 保存完成后关闭曲线段并关联称重记录ID。
/// </summary>
private void CurveCollector_EndSegment(string opName, long weighingRecordId)
{
if (!_curveCollectors.TryGetValue(opName, out CurveCollectorState state)) return;
CurveCollector_StopSampling(opName);
if (!state.IsSegmentEnded)
{
B_DB_Opera.Curve_EndSegment(state.SegmentId, weighingRecordId);
state.IsSegmentEnded = true;
WeighingPage?.AppendLog($"[{opName}] 实时重量曲线采集结束段ID={state.SegmentId}");
}
_curveCollectors.TryRemove(opName, out _);
}
/// <summary>
/// 将采集缓冲区剩余点写入数据库。
/// </summary>
private void CurveCollector_Flush(CurveCollectorState state)
{
List<CurveSamplePoint> writePoints = null;
lock (state.BufferLock)
{
if (state.Buffer.Count > 0)
{
writePoints = new List<CurveSamplePoint>(state.Buffer);
state.Buffer.Clear();
}
}
if (writePoints != null)
{
B_DB_Opera.Curve_WritePoints(state.SegmentId, writePoints);
}
}
}
}