同步现场最新程序

This commit is contained in:
XingCheng3
2026-05-31 10:22:04 +08:00
parent 02e3b2f302
commit 92c8f2d68d
19 changed files with 1213 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ namespace MesDataFunction
public static string PrintURL { get; set; }
public static int IsDemoMesServer { get; set; }
public static Dictionary<string, string[]> SpecialOpList { get; set; }
public static Dictionary<string, Dictionary<string, Dictionary<string, string>>> SpecialOpRuleConfig { get; set; }
}
}

View File

@@ -0,0 +1,360 @@
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;
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = "relative:MesServer.Function\\Function08"
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{B5A0BAA3-C1A7-4AAB-9486-2E650ADACA7E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Function1062</RootNamespace>
<AssemblyName>Function1062</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="BasicData">
<HintPath>..\..\dll_common\BasicData.dll</HintPath>
</Reference>
<Reference Include="MesWork.DeviceDriver, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\dll_common\MesWork.DeviceDriver.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\dll_common\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http">
<HintPath>..\..\DLL\System.Net.Http.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DataAccess\DataAccess.cs" />
<Compile Include="MesServerFunction\Function1062.cs" />
<Compile Include="OpcServer\EngineGetOver.cs" />
<Compile Include="OpcServer\HeartBeat.cs" />
<Compile Include="OpcServer\MesRead.cs" />
<Compile Include="OpcServer\PartLoad.cs" />
<Compile Include="OpcServer\WebSubmit.cs" />
<Compile Include="OpcServer\WorkStart.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\09MesDataFunction\MesDataFunction\01-MesDataFunction.csproj">
<Project>{90e76fb4-646d-45e2-95db-0b8ab4e465d6}</Project>
<Name>01-MesDataFunction</Name>
</ProjectReference>
<ProjectReference Include="..\..\Common\MyLog4Net\00_MyLog4Net.csproj">
<Project>{1dc48c83-1842-4de8-acaf-26e1e7f7c58f}</Project>
<Name>00_MyLog4Net</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
</PropertyGroup>
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,128 @@
using MesServerWork;
using MisDataFunDll;
using System;
using System.Collections.Generic;
using System.Threading;
namespace MesServerFunctions
{
public partial class FunctionMES
{
public void FunWork(string recData)
{
List<string> dataArray = MisDataFun.GetCmdStringList(recData);
string source = dataArray[0].ToString();
string command = dataArray[1].ToString();
string opName = dataArray[2].ToString();
string value = dataArray[3].ToString();
var e = new DeviceDriver_BasicData.CustomeEvetnArgs();
switch (source)
{
case "WEB":
e.OpName = opName;
e.TagValue = command + "|" + value;
fun_WebSubmit(e);
break;
case "PLC":
e = Newtonsoft.Json.JsonConvert.DeserializeObject<DeviceDriver_BasicData.CustomeEvetnArgs>(value);
e.TagValue = NormalizeTagValue(e.TagValue);
RoutePlcSignal(e, opName);
break;
}
}
private string NormalizeTagValue(object tagValue)
{
string value = Convert.ToString(tagValue);
switch (value.ToLower())
{
case "false":
return "0";
case "true":
return "1";
default:
return value;
}
}
private void RoutePlcSignal(DeviceDriver_BasicData.CustomeEvetnArgs e, string opName)
{
switch (e.TagTypeCodeID.ToString())
{
// PLC 工件到位
case "2":
fun_WorkStart(e);
break;
// PLC 允许保存质量数据
case "19":
fun_MesRead(e);
break;
// PLC 申请上线
case "43":
PartLoad(e);
break;
// PLC 传递机型
case "44":
fun_EngineTypeGetOverPLC(e);
break;
// PLC/MES 系统健康心跳
case "14":
fun_HeartBeatMIS(e);
break;
// MES 设备报警复位
case "61":
MIS_BASE.Write_MesErrorType(opName, 0);
break;
}
}
/// <summary>
/// 传递机型
/// </summary>
public void fun_EngineTypeGetOverPLC(DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(EngineTypeGetOver_PLC, e);
}
/// <summary>
/// 保存完成 19
/// </summary>
public void fun_MesRead(DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(MesRead, e);
}
/// <summary>
/// 工件到位离开,清空 MES 信号
/// </summary>
public void fun_WorkStart(DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(WorkStart, e);
}
/// <summary>
/// 处理 WEB/PAD 页面传递过来的数据
/// </summary>
public void fun_WebSubmit(DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(WebSubmit, e);
}
/// <summary>
/// MES 系统健康心跳监测
/// </summary>
public void fun_HeartBeatMIS(DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(HeartBeatMES, e);
}
/// <summary>
/// PLC 系统健康心跳监测
/// </summary>
public void fun_HeartBeatPLC(DeviceDriver_BasicData.CustomeEvetnArgs e)
{
ThreadPool.QueueUserWorkItem(HeartBeatPLC, e);
}
}
}

View File

@@ -0,0 +1,97 @@
using MesServerWork;
using MisDataFunDll;
using MisDataSaveDate;
using System;
namespace MesServerFunctions
{
partial class FunctionMES
{
object lockBarcodeEngineTypeGetOver = new object();
/// <summary>
/// OP431 PLC 回传机型后,校验 PAD 确认信息并调用工厂 MES 进站。
/// </summary>
private void EngineTypeGetOver_PLC(object e)
{
lock (lockBarcodeEngineTypeGetOver)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
string opName = Convert.ToString(msgEvent.OpName);
int tagValue = Convert.ToInt32(msgEvent.TagValue);
if (tagValue != 1)
{
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "PLC传递机型取消");
MIS_BASE.Write_FixAllow(opName, false);
return;
}
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "PLC传递机型");
MIS_BASE.Write_TagValueByTagTypeID(34, opName, false);
MIS_BASE.GetEngineTypeFromPLC(opName, out int engineTypeID, out string engineType, out string engineID, out int palletCode, out string orderForm);
MIS_BASE.GetEngineTypeFromPLC_MES(opName, out int engineTypeIDMes, out string engineTypeMes, out string engineIDMes, out string orderFormMes);
if (string.IsNullOrWhiteSpace(engineID) || engineTypeID <= 0)
{
WriteManualStaticEngineError(opName, "PLC产品码或机型为空产品码" + engineID + ",机型代码:" + engineTypeID);
return;
}
if (string.IsNullOrWhiteSpace(engineIDMes) || engineTypeIDMes <= 0)
{
WriteManualStaticEngineError(opName, "未查询到PAD确认上线信息请先在PAD确认上线");
return;
}
if (!string.Equals(engineID.Trim(), engineIDMes.Trim(), StringComparison.OrdinalIgnoreCase))
{
WriteManualStaticEngineError(opName, "PLC产品码与PAD确认产品码不一致PLC" + engineID + "PAD" + engineIDMes);
return;
}
if (engineTypeID != engineTypeIDMes)
{
WriteManualStaticEngineError(opName, "PLC机型与PAD确认机型不一致PLC" + engineTypeID + "PAD" + engineTypeIDMes);
return;
}
if (!TryGetManualStaticOutProduct(engineID, out ManualStaticOutProduct product, out string productMessage))
{
WriteManualStaticEngineError(opName, productMessage);
return;
}
CallWebService.Call_miFindCustomAndSfcData(opName, engineID, out string code, out string message);
if (code != "0")
{
WriteManualStaticEngineError(opName, "进站失败:" + engineID + "code" + code + "message" + message);
return;
}
MisDataFun.LogRecording_OP(opName, "INFO", "MOM", "进站成功:" + engineID);
MisDataFun.OpName_MIS_MOBY_Insert(opName, product.EngineTypeID, product.EngineType, product.EngineID, palletCode, product.OrderForm, 0, 0, "");
MIS_BASE.Write_FixAllow(opName, true);
SendMessage.AsyncSendMessage_Notice(opName, "【" + opName + "】进站成功允许PLC工作" + engineID);
}
catch (Exception err)
{
MyLog4Net.MyLogHelper.Error("Function1062.EngineTypeGetOver", err.Message);
MisDataFun.LogRecording_OP(ManualStaticOutOpName, "ERROR", "EngineTypeGetOver", err.Message);
}
}
}
private void WriteManualStaticEngineError(string opName, string message)
{
MIS_BASE.Write_FixAllow(opName, false);
MIS_BASE.Write_TagValueByTagTypeID(34, opName, true);
SendMessage.AsyncSendMessage_Notice(opName, "【" + opName + "】" + message);
MisDataFun.LogRecording_OP(opName, "ERROR", "EngineTypeGetOver", message);
}
}
}

View File

@@ -0,0 +1,56 @@
using MesServerWork;
using System;
namespace MesServerFunctions
{
partial class FunctionMES
{
/// <summary>
/// MES与PLC心跳监听
/// </summary>
/// <param name="OpName"></param>
/// <param name="tagValue"></param>
private void HeartBeatMES(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
string OpName = Convert.ToString(msgEvent.OpName);
int tagValue = Convert.ToInt32(msgEvent.TagValue);
//返写在MES心跳中
MIS_BASE.Write_TagValueByTagTypeID(15, OpName, tagValue);
}
catch (Exception err)
{
MyLog4Net.MyLogHelper.Error("Function1062.HeartBeatMES", err.Message);
}
}
/// <summary>
/// MES与PLC心跳监听
/// </summary>
/// <param name="OpName"></param>
/// <param name="tagValue"></param>
private void HeartBeatPLC(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
string OpName = Convert.ToString(msgEvent.OpName);
int tagValue = Convert.ToInt32(msgEvent.TagValue);
int HeartBeatPLC;
//读取PLC心跳数据
HeartBeatPLC = Convert.ToInt32(MIS_BASE.Read_TagValueByTagTypeID(14, OpName));
//通知WEB修改状态
string message = "MES|HeartBeatPLC|" + OpName + "|" + HeartBeatPLC.ToString();
SendMessage.AsyncSendMessage(message);
}
catch (Exception err)
{
MyLog4Net.MyLogHelper.Error("Function1062.HeartBeatPLC", err.Message);
}
}
}
}

View File

@@ -0,0 +1,109 @@
using MesServerWork;
using MisDataFunDll;
using MisDataSaveDate;
using System;
using System.Data;
namespace MesServerFunctions
{
partial class FunctionMES
{
/// <summary>
/// OP431 保存:按 PLC 允许保存时间生成静置质量数据,正常过站后带质量数据出站。
/// </summary>
private void MesRead(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
string opName = Convert.ToString(msgEvent.OpName);
int tagValue = Convert.ToInt32(msgEvent.TagValue);
if (tagValue != 1)
{
MIS_BASE.Write_MesReadOver(opName, false);
MIS_BASE.Write_MesWorkFinish(opName, false);
MIS_BASE.Write_OpNameIsOk(opName, -99);
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "PLC允许保存数据取消");
return;
}
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "PLC允许保存OP431静置数据");
MIS_BASE.GetEngineTypeFromPLC(opName, out int engineTypeID, out string engineType, out string engineID, out int palletCode, out string orderForm);
if (string.IsNullOrWhiteSpace(engineID))
{
WriteManualStaticMesReadError(opName, "PLC产品码为空无法保存静置数据");
return;
}
MisDataFun.Moby_Select_PDC(engineID, out orderForm, out engineType, out engineTypeID);
if (engineTypeID <= 0)
{
WriteManualStaticMesReadError(opName, "未查询到产品执行信息:" + engineID);
return;
}
MIS_BASE.Read_QualityMask(opName, out int qualityMask);
qualityMask = NormalizeManualStaticQualityMask(qualityMask);
if (!TryBuildManualStaticOutData(engineID, qualityMask, out ManualStaticOutData staticOutData, out string message))
{
WriteManualStaticMesReadError(opName, message);
return;
}
if (!WriteManualStaticOutQualityData(opName, engineID, staticOutData, out message))
{
WriteManualStaticMesReadError(opName, message);
return;
}
MisDataFun.WorkStartInsert(opName, qualityMask);
MisDataFun.GetPartQualityGroupParams(opName, engineID, out DataTable dtQuality);
if (dtQuality == null || dtQuality.Rows.Count == 0)
{
WriteManualStaticMesReadError(opName, "未查询到OP431静置质量数据组禁止静默出站" + engineID);
return;
}
CallWebService.Call_dataCollectForSfcEx(opName, engineID, dtQuality, qualityMask, out string code, out string momMessage);
if (code == "0")
{
MIS_BASE.Write_OpNameIsOk(opName, qualityMask);
}
else if (code == "13921")
{
qualityMask = 2;
MIS_BASE.Write_OpNameIsOk(opName, qualityMask);
MisDataFun.LogRecording_OP(opName, "INFO", "MOM", engineID + " 工厂MES质量判定不合格按不合格完成OP431过站");
}
else
{
WriteManualStaticMesReadError(opName, "出站失败:" + engineID + ",错误代码:" + code + ",消息:" + momMessage);
return;
}
MIS_BASE.Write_MesWorkFinish(opName, true);
MIS_BASE.Write_MesReadOver(opName, true);
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "MES保存OP431静置数据完成");
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "MES工作完成");
}
catch (Exception err)
{
MyLog4Net.MyLogHelper.Error("Function1062.MesRead", err.Message);
MisDataFun.LogRecording_OP(ManualStaticOutOpName, "ERROR", "MesRead", err.Message);
}
}
private void WriteManualStaticMesReadError(string opName, string message)
{
MIS_BASE.Write_MesReadOver(opName, false);
MIS_BASE.Write_MesWorkFinish(opName, false);
MIS_BASE.Write_TagValueByTagTypeID(34, opName, true);
SendMessage.AsyncSendMessage_Notice(opName, "【" + opName + "】" + message);
MisDataFun.LogRecording_OP(opName, "ERROR", "MesRead", message);
}
}
}

View File

@@ -0,0 +1,44 @@
using MesServerWork;
using MisDataFunDll;
using System;
namespace MesServerFunctions
{
partial class FunctionMES
{
/// <summary>
/// OP431 申请上线:只记录 PLC 申请状态,等待 PAD 扫描已有产品。
/// </summary>
private void PartLoad(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
string opName = Convert.ToString(msgEvent.OpName);
int tagValue = Convert.ToInt32(msgEvent.TagValue);
if (tagValue == 1)
{
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "PLC申请上线等待PAD扫描已有产品");
MIS_BASE.Write_TagValueByTagTypeID(34, opName, false);
SendMessage.AsyncSendMessage_Notice(opName, "【" + opName + "】PLC已申请上线请PAD扫描产品码");
// PAD 页面可能未打开,申请上线状态由 PadScanQuery 当前读取后返回。
SendMessageFun.SendInfomation("PartLoad", "1", opName);
return;
}
MisDataFun.LogRecording_OP(opName, "INFO", "MIS", "PLC申请上线取消");
MIS_BASE.Write_EngineTypeGetOver_MES(opName, false);
MIS_BASE.Write_FixAllow(opName, false);
MIS_BASE.Write_TagValueByTagTypeID(34, opName, false);
// 不向 OP431PAD 发送实时取消消息,避免 PAD 依赖可能丢失的 MQTT 状态。
SendMessageFun.SendInfomation("PartLoad", "0", opName);
}
catch (Exception err)
{
MyLog4Net.MyLogHelper.Error("Function1062.PartLoad", err.Message);
MisDataFun.LogRecording_OP(ManualStaticOutOpName, "ERROR", "PartLoad", err.Message);
}
}
}
}

View File

@@ -0,0 +1,183 @@
using MesServerWork;
using MisDataFunDll;
using MisDataSaveDate;
using System;
using System.Collections;
namespace MesServerFunctions
{
partial class FunctionMES
{
/// <summary>
/// 处理 PAD/MQTT 发送到 Function1062 的 WEB 命令。
/// 消息外层格式由通讯层统一为WEB|命令|工位号|参数。
/// 命令名不增加 OP431 前缀,工位归属统一通过 opName 判断,避免同一业务出现多套命令名。
/// </summary>
private void WebSubmit(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
string opName = Convert.ToString(msgEvent.OpName);
string content = Convert.ToString(msgEvent.TagValue);
// Function 入口已将 MQTT 的 Command 和 Value 合并为“命令|参数”。
// 参数支持“产品码”或“产品码^最小静置分钟数”,订单、机型等业务信息必须由后端重新查询。
string[] dataArray = content.Split('|');
if (dataArray.Length != 2)
{
MisDataFun.LogRecording_OP(opName, "ERROR", "WEB", "PAD命令格式错误内容" + content);
return;
}
string command = dataArray[0].Trim();
string value = dataArray[1].Trim();
switch (command)
{
// PAD 扫码查询:只查询产品、前序离开时间和静置分钟数,不写 PLC也不调用工厂 MES 进站。
case "PadScanQuery":
{
if (!TryGetManualStaticPadRequest(opName, "PadScanQuery", value, out string engineID, out decimal minStaticMinutes))
{
return;
}
bool success = QueryManualStaticOutProduct(opName, engineID, minStaticMinutes, out string message);
SendManualStaticOutResult(opName, "PadScanQuery", success, message);
MisDataFun.LogRecording_OP(opName, success ? "INFO" : "WARNING", "PadScanQuery", message);
break;
}
// PAD 确认上线重新校验托盘到位、PLC申请上线和前端配置的最小静置时间通过后写 PLC 产品区。
case "PadConfirmOnline":
{
if (!TryGetManualStaticPadRequest(opName, "PadConfirmOnline", value, out string engineID, out decimal minStaticMinutes))
{
return;
}
bool success = ConfirmManualStaticOutProduct(opName, engineID, minStaticMinutes, out string message);
SendManualStaticOutResult(opName, "PadConfirmOnline", success, message);
MisDataFun.LogRecording_OP(opName, success ? "INFO" : "ERROR", "PadConfirmOnline", message);
break;
}
// 页面刷新或 PAD 重新订阅 MQTT 后,用当前 PLC/MES 状态恢复界面显示。
case "IniSingalToWeb":
InitializeSingalToWeb(opName);
break;
default:
MisDataFun.LogRecording_OP(opName, "WARNING", "WebSubmit", "不支持的PAD命令" + command);
break;
}
}
catch (Exception err)
{
MyLog4Net.MyLogHelper.Error("Function1062.WebSubmit", err.Message);
MisDataFun.LogRecording_OP(ManualStaticOutOpName, "ERROR", "WebSubmit", err.Message);
}
}
private bool TryGetManualStaticPadRequest(string opName, string command, string value, out string engineID, out decimal minStaticMinutes)
{
engineID = "";
minStaticMinutes = DefaultManualStaticMinutes;
string requestValue = Convert.ToString(value).Trim();
// PAD 上传产品码和可选最小静置分钟数。订单、机型、静置开始时间等信息全部从 MES 数据库重新取。
if (string.IsNullOrWhiteSpace(requestValue))
{
string message = "PAD命令参数为空格式" + command + "|产品码 或 " + command + "|产品码^最小静置分钟数";
SendManualStaticOutResult(opName, command, false, message);
MisDataFun.LogRecording_OP(opName, "ERROR", "WEB", message);
return false;
}
if (requestValue.Contains("&"))
{
string message = "PAD命令参数格式错误格式" + command + "|产品码 或 " + command + "|产品码^最小静置分钟数,收到:" + value;
SendManualStaticOutResult(opName, command, false, message);
MisDataFun.LogRecording_OP(opName, "ERROR", "WEB", message);
return false;
}
string[] requestItems = requestValue.Split('^');
if (requestItems.Length > 2)
{
string message = "PAD命令参数格式错误格式" + command + "|产品码 或 " + command + "|产品码^最小静置分钟数,收到:" + value;
SendManualStaticOutResult(opName, command, false, message);
MisDataFun.LogRecording_OP(opName, "ERROR", "WEB", message);
return false;
}
engineID = requestItems[0].Trim();
if (string.IsNullOrWhiteSpace(engineID))
{
string message = "PAD命令产品码为空格式" + command + "|产品码 或 " + command + "|产品码^最小静置分钟数";
SendManualStaticOutResult(opName, command, false, message);
MisDataFun.LogRecording_OP(opName, "ERROR", "WEB", message);
return false;
}
if (requestItems.Length == 2)
{
string minStaticMinutesText = requestItems[1].Trim();
if (!decimal.TryParse(minStaticMinutesText, out minStaticMinutes) || minStaticMinutes < 0)
{
string message = "PAD命令最小静置分钟数无效收到" + minStaticMinutesText;
SendManualStaticOutResult(opName, command, false, message);
MisDataFun.LogRecording_OP(opName, "ERROR", "WEB", message);
return false;
}
}
return true;
}
/// <summary>
/// 页面重新登录或 PAD 重新订阅时恢复关键状态。
/// 这里发送的是通用命令名,工位由 opName 区分。
/// </summary>
private void InitializeSingalToWeb(string opName)
{
MIS_BASE.GetEngineTypeFromDB(opName, out int engineTypeID, out string engineType, out string engineID, out int partPalletCode);
MIS_BASE.Read_WorkStart(opName, out bool workStart);
MIS_BASE.Read_PartLoad(opName, out bool partLoad);
MIS_BASE.Read_EngineGetOver_PLC(opName, out bool engineGetOver);
MIS_BASE.Read_FixAllow(opName, out bool fixAllow);
MIS_BASE.Read_QualityData_Read_CF(opName, out bool mesRead);
MIS_BASE.Read_MesReadOver(opName, out bool readOver);
MIS_BASE.Read_MaterialVerifyOver(opName, out bool workFinish);
MIS_BASE.Read_QualityMask(opName, out int qualityMask);
MIS_BASE.Read_FalseGo(opName, out bool falseGo);
if (!string.IsNullOrWhiteSpace(engineID))
{
SendMessageFun.SendMessage("5123", "1111", opName);
}
// 以下状态沿用通用 MES 页面命令PAD 或页面通过 opName 判断属于哪个工位。
SendMessageFun.SendInfomation("WorkStart", Convert.ToString(Convert.ToInt32(workStart)), opName);
SendMessageFun.SendInfomation("PartLoad", Convert.ToString(Convert.ToInt32(partLoad)), opName);
SendMessageFun.SendInfomation("EngineTypeGetOverPLC", Convert.ToString(Convert.ToInt32(engineGetOver)), opName);
SendMessageFun.SendInfomation("FixAllow", Convert.ToString(Convert.ToInt32(fixAllow)), opName);
SendMessageFun.SendInfomation("MesRead", Convert.ToString(Convert.ToInt32(mesRead)), opName);
SendMessageFun.SendInfomation("MesReadOver", Convert.ToString(Convert.ToInt32(readOver)), opName);
SendMessageFun.SendInfomation("MaterialVerifyOver", Convert.ToString(Convert.ToInt32(workFinish)), opName);
SendMessageFun.SendInfomation("QualityMask", Convert.ToString(qualityMask), opName);
SendMessageFun.SendInfomation("FalseGO", Convert.ToString(Convert.ToInt32(falseGo)), opName);
SendMessageFun.SendInfomation("CurrentProduct", engineID + "^" + engineTypeID + "^" + engineType + "^" + partPalletCode, opName);
InitAlarm(opName);
}
private void InitAlarm(string opName)
{
FuntionMesRead.Alarm_InitAlarm_Send(opName, out Hashtable ht);
foreach (DictionaryEntry kv in ht)
{
FuntionMesRead.Send_AlarmMsg(kv.Key.ToString(), kv.Value.ToString());
}
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using MesServerWork;
using MisDataFunDll;
//using SystemFramework;
namespace MesServerFunctions
{
partial class FunctionMES
{
/// <summary>
/// 工件到位与离开时清空MES自身信号
/// </summary>
/// <param name="OpName"></param>
/// <param name="tagValue"></param>
private void WorkStart(object e)
{
try
{
var msgEvent = (DeviceDriver_BasicData.CustomeEvetnArgs)e;
string OpName = Convert.ToString(msgEvent.OpName);
string tagValue = msgEvent.TagValue.ToString();
if (tagValue == "1")
{
string msg = "工件到位!";
MisDataFun.LogRecording_OP(OpName, "INFO", "MIS", msg);
MisDataFun.MOBY_TrayStart_Update(OpName); // 记录托盘到达时间
MIS_BASE.Write_TagValueByTagTypeID(34, OpName, false); // MES报警点
}
else
{
string msg = "工件离开!";
MisDataFun.LogRecording_OP(OpName, "INFO", "MIS", msg);
// 清除工位数据
MIS_BASE.Write_EngineTypeGetOver_MES(OpName, false); // 传递机型MES
MIS_BASE.Write_MesWorkFinish(OpName, false); // 工作完成MES
MIS_BASE.Write_FalseGO(OpName, false); // 不合格放行MES
MIS_BASE.Write_ConfirmOkGo(OpName, false); // 合格放行MES
MIS_BASE.Write_FixAllow(OpName, false); // 允许PLC工作MES
MIS_BASE.Write_MesReadOver(OpName, false); // MES保存完数据MES
MIS_BASE.Write_EngineTypeID_MES(OpName, 0); // 机型标志MES
MIS_BASE.Write_EngineID_MES(OpName, ""); // 工件编号MES
MIS_BASE.Write_EngineType_MES(OpName, ""); // 产品型号MES
MIS_BASE.Write_OrderForm_MES(OpName, ""); // 订单号(MES)
MIS_BASE.Write_MesErrorType(OpName, 0); // MES报警点
}
}
catch (Exception err)
{
MisDataFun.LogRecording_OP("Function1062", "ERROR", "WorkStart", err.Message.ToString());
}
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息通过以下
// 特性集控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("Function08")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("大连迈思信息技术有限公司")]
[assembly: AssemblyProduct("Function08")]
[assembly: AssemblyCopyright("Copyright © 大连迈思信息技术有限公司 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 使此程序集中的类型
// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
// 则将该类型上的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("47710f2c-e141-4252-8d13-701bf4c1c027")]
// 程序集的版本信息由下面四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
// 方法是按如下所示使用“*”:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Binary file not shown.

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="MQTTnet" publicKeyToken="b69712f52770c0a7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.13.0" newVersion="3.0.13.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup></configuration>

View File

@@ -0,0 +1,5 @@
SCC = This is a Source Code Control file
[Function08.csproj]
SCC_Aux_Path = "\\192.168.0.100\vss\MES4.0"
SCC_Project_Name = "$/MisData/MisData/MisData/MesServer.Function/Function08", KRHAAAAA

Binary file not shown.