Files
1257B_DB/JinYuan.ControlCenter/ControlCenter.cs
T

2040 lines
86 KiB
C#
Raw Normal View History

2026-08-06 09:23:30 +08:00
using JinYuan.Helper;
using JinYuan.MES.Models;
using JinYuan.Models;
using JinYuan.VirtualDataLibrary;
using Language;
using MiniExcelLibs;
using PLC;
using PLC.PLCAlarm;
using PLCCommunication;
using PLCCommunication.Common;
using PLCCommunication.Common.DataConvert;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Runtime.CompilerServices.RuntimeHelpers;
using AlarmData = JinYuan.Models.AlarmData;
using DataType = PLC.DataType;
namespace JinYuan.ControlCenters
{
public partial class ControlCenter : IDisposable
{
#region 1、变量定义
private static readonly object uiLock = new object();
private static ControlCenter _instance;
public static ControlCenter Instance
{
get
{
lock (uiLock)
{
return _instance ??= new ControlCenter();
}
}
}
private readonly Dictionary<string, PlcGroup> groups = new Dictionary<string, PlcGroup>();
private Dictionary<string, Func<JYResult<byte[]>, PlcGroup, Task>> stationFunctions;
//private Dictionary<string, Action<JYResult<byte[]>, PlcGroup>> stationFunctions;
private readonly Dictionary<PlcReadWriteBase, List<PlcDevice>> plcDeviceMapping = new Dictionary<PlcReadWriteBase, List<PlcDevice>>();
private readonly Dictionary<string, Task> groupTasks = new Dictionary<string, Task>();
private readonly Dictionary<PlcReadWriteBase, PLCAlarmReader> plcAlarmReaders = new Dictionary<PlcReadWriteBase, PLCAlarmReader>();
public CancellationTokenSource _cts;
private Task _heartbeatTask;
private const int HeartbeatInterval = 1000; // 1 second
private Dictionary<string, int> group_status = new Dictionary<string, int>();
/// <summary>
/// 定时器-定时刷新状态和数据
/// </summary>
private System.Timers.Timer Timer_RefreshData;
private readonly string configFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config");
#endregion
#region 2、初始化函数
/// <summary>
/// 构造函数
/// </summary>
private ControlCenter()
{
_cts = new CancellationTokenSource();
if (CommonMethods.plcDevices == null)
{
CommonMethods.plcDevices = new List<PlcReadWriteBase>();
}
InitializeDevicesFromExcel();
GetAlarmData();
InitFunc();
//initPrint();//打印机初始化
Timer_RefreshData = new System.Timers.Timer(1000);
Timer_RefreshData.Elapsed += Timer_RefreshData_Elapsed;
Timer_RefreshData.Start();
// 轴位置监控
//Task.Run(() => { ShowAxleEven_Run(); });
// 电机位置监控
//Task.Run(() => { ShowMotorEven_Run(); });
//Mes心跳监控
Task.Run(new Action(() => { UpdateMesHeartBeat(); }));
//删除30天内日志
//DeleteLog.Start(CommonMethods.strDeletepath);//会把整个ApiLog删除
string[] clrPath = new string[] { CommonMethods.strAlarmLogspath, CommonMethods.strMesLogspath, CommonMethods.strWattrMeterpath, Application.StartupPath + "\\Logs\\" };
short[] seltWay = new short[] { 0, 0, 0, 1 };
DeleteLog.Start(clrPath, seltWay);
if (!CommonMethods.IsRun)
{
CommonMethods.ResetEventRecv.Reset();
}
}
/// <summary>
/// 初始化 工位方法,添加到字典里面
/// </summary>
private void InitFunc()
{
stationFunctions = new Dictionary<string, Func<JYResult<byte[]>, PlcGroup, Task>>
{
["包装箱进站"] = FeedingStation, // 线头扫描,包装箱进站
["中部扫描"] = FeedingStationB, // 中部扫描
["尾部扫描"] = FeedingStationC, // 尾部扫描
//["箱唛标签打印"] = PrintContainerStation, // 箱唛打印
//["栈板标签打印"] = PrintPalletStation, // 栈板打印,O2不打印
//["3S标签打印"] = Print3SStation, // 3S打印 在线网站拿数据的,贴标打印机自行打印
//["6J标签打印"] = Print6JStation, // 6J打印
//["绑定客户标签"] = BindingLabelStation, // 绑定客户标签
["包装箱出站"] = BlankingStation, // 包装箱出站
["中转数据"] = TransferStation, // 中转数据
};
}
/// <summary>
/// 启动心跳及工位读取线程
/// </summary>
/// <returns></returns>
public async Task StartAsync()
{
CreatePLCThreads();
_heartbeatTask = StartHeartbeatCheck(_cts.Token);
await Task.CompletedTask;
}
/// <summary>
/// 加载配置文件
/// </summary>
private void InitializeDevicesFromExcel()
{
CommonMethods.AddDataMonitorLog(1, "开始加载配置参数……".Translated());
string deviceConfigPath = Path.Combine(configFolderPath, "Device.xlsx");
string groupConfigPath = Path.Combine(configFolderPath, "Group.xlsx");
string variableConfigPath = Path.Combine(configFolderPath, "Variable.xlsx");
//string plcVariableConfigPath = Path.Combine(configFolderPath, "PLCVariable.xlsx");
if (!File.Exists(deviceConfigPath) || !File.Exists(groupConfigPath) || !File.Exists(variableConfigPath))
{
LogHelper.Instance.WriteError("配置文件不存在");
return;
}
try
{
var deviceConfigs = MiniExcel.Query<PlcDevice>(deviceConfigPath).ToList();
var groupConfigs = MiniExcel.Query<PlcGroup>(groupConfigPath).ToList();
var variableConfigs = MiniExcel.Query<PlcVariable>(variableConfigPath).ToList();
foreach (var deviceConfig in deviceConfigs.Where(d => d.IsActive))
{
IPEndPoint iep = new IPEndPoint(IPAddress.Parse(deviceConfig.IPAddress), deviceConfig.Port);
PlcReadWriteBase plc = PLCFactory.CreatePLC(GetPLCTypeEnum(deviceConfig.PLCType), deviceConfig.PlcNum, $"PLC_{deviceConfig.PlcNum}", iep);
deviceConfig.Cts = new CancellationTokenSource();
deviceConfig.GroupList = groupConfigs
.Where(g => g.IsActive && g.PlcNum == deviceConfig.PlcNum)
.ToList();
foreach (var group in deviceConfig.GroupList)
{
group.VarList = variableConfigs
.Where(v => v.GroupName == group.GroupName && v.PlcNum == deviceConfig.PlcNum)
.ToList();
group_status[group.GroupName] = 0;
}
plcDeviceMapping[plc] = new List<PlcDevice> { deviceConfig };
CommonMethods.plcDevices.Add(plc);
// 初始化 PLCAlarm Reader报警读取
var alarmReader = new PLCAlarmReader(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AlarmConfig.csv"),
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AlarmRecords"),
RecordType.RecordWhenFinish,
false
);
//alarmReader.PLCAlarm_New += AlrmReader_PLCAlarm_New;
//alarmReader.PLCAlarm_Remove += AlrmReader_PLCAlarm_Remove;
plcAlarmReaders[plc] = alarmReader;
CommonMethods.plcStatuses[plc] = new Dictionary<string, object>
{
["OldStatus"] = (short)0,
["StatusCount"] = 0,
["GuidAlarm"] = string.Empty,
["Status"] = new List<bool> { false, false, false, false }
};
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"加载配置失败:{ex.Message}");
}
CommonMethods.AddDataMonitorLog(1, "配置文件加载完成".Translated());
}
#endregion
#region 3、PLC读写
#region 创建PLC线程 几个工位就几个线程
/// <summary>
/// 按工位读取线程
/// </summary>
///
private readonly Dictionary<string, Thread> groupThreads = new Dictionary<string, Thread>();
private readonly object lockObject = new object();
private void CreatePLCThreads()
{
for (int i = 0; i < CommonMethods.plcDevices.Count; i++)
{
var plc = CommonMethods.plcDevices[i];
if (plcDeviceMapping.TryGetValue(plc, out var plcDevices))
{
foreach (var plcDevice in plcDevices)
{
foreach (var plcGroup in plcDevice.GroupList.Where(g => g.IsActive))
{
string threadKey = $"{plcDevice.PlcNum}-{plcGroup.GroupName}";
var parameter = (group: plcGroup, plcIndex: i);
Thread thread = new Thread(new ParameterizedThreadStart(ReadPLCWorkModel));
thread.IsBackground = true;
thread.Priority = ThreadPriority.Highest;
lock (lockObject)
{
groupThreads[threadKey] = thread;
}
thread.Start(parameter);
}
}
}
}
}
#endregion
#region 循环读取PLC工位状态
/// <summary>
/// 读取PLC循环工位
/// </summary>
/// <param name="plcModel"></param>
///
private void ReadPLCWorkModel(object parameter)
{
if (parameter is ValueTuple<PlcGroup, int> tuple)
{
var (group, plcIndex) = tuple;
while (!_cts.Token.IsCancellationRequested)
{
if (CommonMethods.IsRun)
{
if (CommonMethods.plcDevices[plcIndex].IsConnected)
{
try
{
JYResult<short> OpenValue = CommonMethods.plcDevices[plcIndex].ReadValue<JYResult<short>>(group.VarList[0].VarAddress, DataType.Short);
if (OpenValue.IsSuccess && OpenValue.Content == 1 && group_status[group.GroupName] == 0)
{
group_status[group.GroupName] = 1;
JYResult<byte[]> resByte = CommonMethods.plcDevices[plcIndex].ReadValue<JYResult<byte[]>>($"{group.VarList[2].VarAddress}",
DataType.ArrByte, Convert.ToUInt16(group.VarList[2].OffsetOrLength));
if (resByte != null && stationFunctions.TryGetValue(group.GroupName, out var action))
{
Task.Run(() => action(resByte, group));
}
}
else if (OpenValue.IsSuccess && OpenValue.Content == 0) // 触发地址被PLC清0后的处理处理
{
if(group_status[group.GroupName] != 0)
{
group_status[group.GroupName] = 0;
CommonMethods.AddDataMonitorLog(0, $"{group.GroupName} PLC设置为0,清空状态为0");
}
JYResult<short> checkValue = CommonMethods.plcDevices[plcIndex].ReadValue<JYResult<short>>(group.VarList[1].VarAddress, DataType.Short);
if (checkValue.IsSuccess && checkValue.Content != 0)
{
CommonMethods.plcDevices[plcIndex].WriteValue(group.VarList[1].VarAddress, 0, DataType.Short);
LogHelper.Instance.WriteLog($"记录: {group.VarList[1].VarAddress}清0");
}
}
}
catch (Exception ex)
{
group_status[group.GroupName] = 0;
LogHelper.Instance.WriteError($"错误: {group.GroupName} - {ex.Message}");
}
}
}
Thread.Sleep(50);
}
}
}
#endregion
#region 开始停止联机事件
/// <summary>
/// 开始联机
/// </summary>
/// <returns></returns>
public async Task<bool> ConnectAllPlcsAsync()
{
bool allConnected = true;
if (CommonMethods.plcDevices.Count <= 0)
{
CommonMethods.AddDataMonitorLog(1, "无PLC配置信息".Translated() + "," + "请检查配置文件".Translated());
return allConnected = false;
}
foreach (var plc in CommonMethods.plcDevices)
{
if (plcDeviceMapping.TryGetValue(plc, out var plcDevices))
{
foreach (var deviceConfig in plcDevices)
{
string msg = "";
try
{
// 使用 Task.Run 来在后台线程执行连接操作
bool isConnected = await Task.Run(() =>
{
// 设置一个连接超时
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))) // 10秒超时
{
try
{
return plc.Connect(ref msg);
}
catch (OperationCanceledException)
{
msg = "连接超时";
return false;
}
}
}).ConfigureAwait(false);
deviceConfig.IsConnected = isConnected;
if (!deviceConfig.IsConnected)
{
allConnected = false;
LogHelper.Instance.WriteLog($"连接PLC失败: {deviceConfig.PlcNum}, 错误: {msg}");
CommonMethods.AddDataMonitorLog(1, "连接PLC失败".Translated() + $": {deviceConfig.PlcNum}, " + "错误".Translated() + $": {msg}");
}
else
{
await SendRightToPLCAsync();
CommonMethods.AddDataMonitorLog(0, "成功连接PLC".Translated() + $": {deviceConfig.PlcNum}");
}
}
catch (Exception ex)
{
allConnected = false;
LogHelper.Instance.WriteLog($"连接PLC时发生异常: {deviceConfig.PlcNum}, 错误: {ex.Message}");
CommonMethods.AddDataMonitorLog(2, "连接PLC时发生异常".Translated() + $": {deviceConfig.PlcNum}" + "错误".Translated() + $": {ex.Message}");
}
}
}
}
return allConnected;
}
/// <summary>
/// 断开链接
/// </summary>
public void DisconnectAllPlcs()
{
foreach (var plc in CommonMethods.plcDevices)
{
if (plcDeviceMapping.TryGetValue(plc, out var plcDevices))
{
foreach (var deviceConfig in plcDevices)
{
plc.Disconnect();
deviceConfig.IsConnected = false;
CommonMethods.AddDataMonitorLog(0, "已断开与PLC".Translated() + $"[{deviceConfig.PlcNum}]" + "的链接".Translated());
}
}
}
}
#endregion
#endregion
#region MES
/// <summary>
/// Mes心跳
/// </summary>
private void UpdateMesHeartBeat()
{
Thread.Sleep(100);
while (true)//true
{
try
{
if (CommonMethods.mesConfig.IsLoginMesOK && CommonMethods.IsRun)//CommonMethods.mesConfig.isUpMes &&
{
if (Ping() == true)
{
CommonMethods.mesConfig.isConnected = true;
}
else//Ping失败,提示检查线路。
{
CommonMethods.mesConfig.isConnected = false;
if (DateTime.Now.Second % 10 == 0)
{
string errStr = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")},Mes心跳Ping失败,IP:{CommonMethods.mesConfig.MESURLAddressIP},端口号:{CommonMethods.mesConfig.MESURLAddressPort}";
TxtHelper.WriteTxt($@"D:\APILog\Logs\MesLogs\{DateTime.Now.ToString("yyyyMMdd")}\Mes心跳报警\{DateTime.Now.ToString("HH")}.txt", errStr);
}
}
}
}
catch (Exception ex)
{
string errStr = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")},Mes心跳Ping异常,IP:{CommonMethods.mesConfig.MESURLAddressIP},端口号:{CommonMethods.mesConfig.MESURLAddressPort},原因:{ex.ToString()}";
TxtHelper.WriteTxt($@"D:\APILog\Logs\MesLogs\{DateTime.Now.ToString("yyyyMMdd")}\Mes心跳报警\{DateTime.Now.ToString("HH")}.txt", errStr);
}
Thread.Sleep(1000);
}
}
/// <summary>
/// Ping 检查网络线路问题
/// </summary>
/// <returns></returns>
public bool Ping()
{
try
{
string strIP = CommonMethods.mesConfig.MESURLAddressIP;
int port = Convert.ToInt32(CommonMethods.mesConfig.MESURLAddressPort);
IPAddress ip = IPAddress.Parse(strIP);
IPEndPoint _endPoint = new IPEndPoint(ip, port);
if (_endPoint.Address == null)
{
return false;
}
Ping _ping = new Ping();
int timeout = 1000;
PingReply mPingRcv = _ping.Send(_endPoint.Address, timeout);
return (mPingRcv.Status == IPStatus.Success) ? true : false;
}
catch (Exception ex)
{
string errStr = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")},Mes心跳Ping异常,IP:{CommonMethods.mesConfig.MESURLAddressIP},端口号:{CommonMethods.mesConfig.MESURLAddressPort},原因:{ex.ToString()}";
TxtHelper.WriteTxt($@"D:\APILog\Logs\MesLogs\{DateTime.Now.ToString("yyyyMMdd")}\Mes心跳报警\{DateTime.Now.ToString("HH")}.txt", errStr);
return false;
}
}
#endregion
#region 4、心跳和状态检查
/// <summary>
/// 心跳检测
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task StartHeartbeatCheck(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
if (CommonMethods.IsRun)
{
foreach (var plc in CommonMethods.plcDevices)
{
if (plcDeviceMapping.TryGetValue(plc, out var plcDevices))
{
foreach (var plcDevice in plcDevices)
{
if (plcDevice.IsActive && plcDevice.IsHeartBeat)
{
await CheckHeartbeatForDevice(plc, plcDevice);
//await ShowStateEven_RunAsync();
}
}
}
}
}
await Task.Delay(HeartbeatInterval, cancellationToken);
}
}
/// <summary>
///
/// </summary>
/// <param name="plc"></param>
/// <param name="plcDevice"></param>
/// <returns></returns>
private async Task CheckHeartbeatForDevice(PlcReadWriteBase plc, PlcDevice plcDevice)
{
if (plcDevice.IsConnected)
{
JYResult result = await Task.Run(() => plc.ReadValue<JYResult<short>>(plcDevice.HeartBeat, DataType.Short));
if (result.IsSuccess)
{
plcDevice.ErrorTimes = 0;
JYResult res = await Task.Run(() => plc.WriteValue(plcDevice.HeartBeat, (short)1, DataType.Short));
if (!res.IsSuccess)
{
plcDevice.IsConnected = false;
LogHelper.Instance.WriteLog($"PLC编号:{plcDevice.PlcNum}=心跳写入异常:{res.Message}!!!");
}
}
else
{
HandleHeartbeatFailure(plcDevice, result);
}
}
else
{
await ReconnectDevice(plc, plcDevice);
}
}
private void HandleHeartbeatFailure(PlcDevice plcDevice, JYResult result)
{
plcDevice.ErrorTimes++;
plcDevice.IsConnected = false;
if (plcDevice.ErrorTimes >= plcDevice.AllowErrorTimes)
{
LogHelper.Instance.WriteLog($"编号:[{plcDevice.PlcNum}]的PLC链接断开:{result.Message}");
}
}
private async Task ReconnectDevice(PlcReadWriteBase plc, PlcDevice plcDevice)
{
if (!plcDevice.IsFirstConnect)
{
await Task.Delay(plcDevice.ReConnectTime);
plc.Disconnect();
}
string msg = "";
var connectResult = await Task.Run(() => plc.Connect(ref msg));
plcDevice.IsConnected = connectResult;
LogHelper.Instance.WriteLog(connectResult
? $"PLC编号:{plcDevice.PlcNum}{(plcDevice.IsFirstConnect ? "初次" : "重新")}连接成功{msg}!"
: $"[{plcDevice.PlcNum}]#PLC{(plcDevice.IsFirstConnect ? "初次" : "重新")}连接失败{msg}!");
plcDevice.IsFirstConnect = false;
}
#endregion
#region 5、报警处理 ×
//private async Task CheckAlarmsAsync(PlcReadWriteBase plc, string guidAlarm)
//{
// if (plc == null)
// {
// CommonMethods.AddDataMonitorLog(2, "PLC 对象为 null".Translated());
// return;
// }
// if (!plc.IsConnected)
// {
// CommonMethods.AddDataMonitorLog(2, "PLC 未连接".Translated());
// return;
// }
// if (plcAlarmReaders.TryGetValue(plc, out var alarmReader))
// {
// try
// {
// JYResult<ushort[]> result = null;
// // 使用 Task.Run 和超时机制
// var task = Task.Run(() =>
// {
// try
// {
// return plc.ReadValue<JYResult<ushort[]>>("D10000", DataType.ArrUshort, 20);
// }
// catch (Exception ex)
// {
// CommonMethods.AddDataMonitorLog(2, "PLC 读取过程中发生异常".Translated() + $": {ex.Message}");
// return null;
// }
// });
// // 设置5秒超时
// if (await Task.WhenAny(task, Task.Delay(5000)) == task)
// {
// result = await task;
// }
// else
// {
// throw new TimeoutException("PLC 读取操作超时");
// }
// if (result == null)
// {
// throw new InvalidOperationException("PLC 读取操作返回空结果");
// }
// if (!result.IsSuccess)
// {
// throw new InvalidOperationException($"PLC 读取操作失败: {result.Message}");
// }
// if (result.Content == null)
// {
// throw new InvalidOperationException("PLC 读取操作返回空内容");
// }
// ushort[] alarmData = result.Content;
// // 使用 alarmData 进行后续操作
// bool isAlarming = alarmReader.IsPLCAlarm(10000, 20, alarmData);
// if (isAlarming)
// {
// foreach (var alarm in alarmReader.RecordAlarms)
// {
// CommonMethods.AddDataMonitorLog(1, $"当前报警: 代码 {alarm.AlarmCode}, 内容 {alarm.AlarmInfo}, 开始时间 {alarm.StartTime}");
// }
// }
// }
// catch (Exception ex)
// {
// CommonMethods.AddDataMonitorLog(2, $"检查报警时发生错误: {ex.Message}");
// }
// }
// else
// {
// CommonMethods.AddDataMonitorLog(2, $"未找到 PLC 的报警读取器");
// }
//}
/// <summary>
/// 新增报警
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
//private void AlrmReader_PLCAlarm_New(object sender, RecordAlarmDataArgs e)
//{
// Task.Run(() =>
// {
// CommonMethods.alarmManager.AddAlarm(e);
// Debug.WriteLine($"新报警: 代码 {e.AlarmCode}, 内容 {e.AlarmInfo}, 日期 {e.AlarmDate}, 开始时间 {e.StartTime:HH:mm:ss}");
// });
//}
/// <summary>
/// 报警结束移除报警
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
//private void AlrmReader_PLCAlarm_Remove(object sender, RecordAlarmDataArgs e)
//{
// Task.Run(() =>
// {
// CommonMethods.alarmManager.RemoveAlarm(e);
// Debug.WriteLine($"报警清除: 代码 {e.AlarmCode}, 内容 {e.AlarmInfo}, 日期 {e.AlarmDate}, 结束时间 {DateTime.Now:HH:mm:ss}");
// });
//}
#endregion
#region 6、设备状态检查 ×
/// <summary>
/// 设备状态
/// </summary>
/// <returns></returns>
//private async Task ShowStateEven_RunAsync()
//{
// try
// {
// string Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
// foreach (var plc in CommonMethods.plcDevices)
// {
// if (plcDeviceMapping.TryGetValue(plc, out var plcDevices))
// {
// foreach (var plcDevice in plcDevices)
// {
// if (plcDevice.IsConnected)
// {
// var plcStatus = CommonMethods.plcStatuses[plc];
// bool isUpload = false;
// string deviceStatus = string.Empty;
// string deviceStatusDesc = string.Empty;
// short newStatus = 100;
// newStatus = await Task.Run(() => plc.ReadValue<JYResult<short>>("D10", DataType.Short).Content);
// if (newStatus != 100)
// {
// if (newStatus != (short)plcStatus["OldStatus"])
// {
// // 从 Alert 切换到非 Alert 状态时,上传报警信息
// if ((short)plcStatus["OldStatus"] == 3 && newStatus != 3)
// {
// await CheckAlarmsAsync(plc, (string)plcStatus["GuidAlarm"]);
// }
// // 只要状态变更,就生成新的 GUID
// plcStatus["GuidAlarm"] = Guid.NewGuid().ToString();
// isUpload = true;
// plcStatus["StatusCount"] = 0;
// plcStatus["OldStatus"] = newStatus;
// }
// else
// {
// plcStatus["StatusCount"] = (int)plcStatus["StatusCount"] + 1;
// if ((int)plcStatus["StatusCount"] >= 5 * 60)
// {
// isUpload = true;
// plcStatus["StatusCount"] = 0;
// }
// }
// (deviceStatus, deviceStatusDesc) = newStatus switch
// {
// 1 => ("Run", "正常运行"),
// 2 => ("Maintain", "停止运行"),
// 3 => ("Alert", "故障调试"),
// 4 => ("Idle", "闲置待机"),
// _ => (string.Empty, string.Empty)
// };
// plcStatus["Status"] = new List<bool> { newStatus == 1, newStatus == 2, newStatus == 3, newStatus == 4 };
// CommonMethods.Statues = new List<bool> { newStatus == 1, newStatus == 2, newStatus == 3, newStatus == 4 };
// if (newStatus == 3) //报警状态
// {
// await CheckAlarmsAsync(plc, (string)plcStatus["GuidAlarm"]);
// }
// else if (newStatus == 4) //闲置状态
// {
// await ShowStuesEven_Run(Date, (string)plcStatus["GuidAlarm"], plc);
// }
// if (isUpload)
// {
// CommonMethods.AddDataMonitorLog(1, $"PLC[{plcDevice.PlcNum}]状态: {deviceStatusDesc}");
// }
// if (CommonMethods.sysConfig.MesModeSwitching)
// {
// // 上传设备状态信息
// if (CommonMethods.sysConfig.MesModeSwitching && isUpload && (newStatus == 1 || newStatus == 3 || newStatus == 4))
// {
// var (success, message) = await CommonMethods.hbgMes.UploadEquStatusAsync(
// CommonMethods.mesConfig.DeviceStatus,
// CommonMethods.mesConfig.siteCode,
// CommonMethods.mesConfig.lineCode,
// CommonMethods.mesConfig.equipNum,
// CommonMethods.mesConfig.MaterialCode,
// CommonMethods.mesConfig.mesUserName,
// strGuidAlarm, // RUN 状态时传空
// deviceStatus,
// null
// );
// if (success)
// {
// StatuesCount = 0;
// }
// }
// }
// }
// }
// }
// }
// }
// }
// catch (Exception ex)
// {
// await Task.Run(() => LogHelper.Instance.WriteEX(ex));
// }
//}
/// <summary>
/// HMI手动触摸屏状态
/// </summary>
/// <param name="Date"></param>
/// <param name="strGuid"></param>
/// <param name="plc"></param>
/// <returns></returns>
//private async Task ShowStuesEven_Run(string Date, string strGuid, PlcReadWriteBase plc)
//{
// if (plc.IsConnected)
// {
// string strCode = "";
// string strText = "";
// short HMIStatus = await Task.Run(() => plc.ReadValue<JYResult<short>>("D40", DataType.Short).Content);
// if (HMIStatus != 0)
// {
// CommonMethods.AddDataMonitorLog(1, "触发了手动HMI弹窗功能!");
// (strCode, strText) = HMIStatus switch
// {
// 1 => ("B1", "计划维保"),
// 2 => ("B2", "无生产计划"),
// 3 => ("B3", "样品、试产"),
// 4 => ("B4", "其他计划停机"),
// 5 => ("D1", "设备故障"),
// 6 => ("D2", "设备转型"),
// 7 => ("D3", "工程变更"),
// 8 => ("D4", "MES停机"),
// 9 => ("D5", "品质异常"),
// 10 => ("D6", "配件、物料更换"),
// 11 => ("D7", "来料不良"),
// 12 => ("D8", "待料"),
// 13 => ("D9", "品质检查"),
// 14 => ("D10", "清洁点检"),
// 15 => ("D11", "环境不满足"),
// 16 => ("D12", "其它非计划停机"),
// _ => (string.Empty, string.Empty)
// };
// if (!string.IsNullOrEmpty(strCode))
// {
// CommonMethods.AddDataMonitorLog(1, $"{strCode} {strText}");
// }
// if (CommonMethods.sysConfig.MesModeSwitching)
// {
// List<AlarmData> alarmObj = new List<AlarmData>();
// AlarmData alarmData = new AlarmData();
// alarmData.AlarmCode = strCode;
// alarmData.StartTime = "";
// alarmData.EndTime = "";
// alarmData.AlarmType = "非计划损失";
// alarmData.AlarmContent = strText;
// alarmObj.Add(alarmData);
// //上传报警
// var (success, message) = await CommonMethods.hbgMes.UploadEquAlarmAsync(CommonMethods.mesConfig.DeviceAlarm, CommonMethods.mesConfig.siteCode, CommonMethods.mesConfig.lineCode,
// CommonMethods.mesConfig.equipNum, CommonMethods.mesConfig.MaterialCode, CommonMethods.mesConfig.mesUserName, alarmObj, strGuid);
// if (success)
// CommonMethods.AddDataMonitorLog(0, $"HMI手动弹窗状态已上传成功,代码:{strCode}");
// }
// bool IsSuccess = CommonMethods.plcDevices[0].WriteInt16("D40", 0);
// if (IsSuccess)
// CommonMethods.AddDataMonitorLog(0, $"D40已复位0");
// }
// }
//}
#endregion
#region 定时刷新数据
int TotalNum = 0;
int TotalNum1 = 0;
int Regular = 0;
int JetBeat = 0;
private void Timer_RefreshData_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (Regular >= 300)
{
Task.Run(() =>
{
UploadEquEnergy();//能耗
//ReUpProducResultParam();//定时补传
});
Regular = 0;
}
if (TotalNum > 4)
{
GetEnergyData();
//GetEnergyDataFormPlc();
Refresh12HourProdQty();
GetDefectTypeQty();
ReadPLCComm();
GetASqlAlarmData(DateTime.Today, CommonMethods.strClass);
TotalNum = 0;
}
else if (TotalNum == 2)//设备状态读取
{
SetClass(); //变更班次
}
Task.Run(() =>
{
ShowStateEven_Run();
});
if (TotalNum1 >= 3600 * 24)
{
Task.Run(() =>
{
DeleteFileOfLocal();
});
TotalNum1 = 0;
}
//电能表心跳
//if (jetBeat > 5)
//{
// if (!CommonMethods.energyMeter.Connected)
// {
// CommonMethods.energyMeter.SetTCPParams(IPAddress.Parse("192.168.1.25"), 502);
// CommonMethods.energyMeter.Connect();
// }
// jetBeat = 0;
//}
//jetBeat++;
TotalNum++;
TotalNum1++;
Regular++;
}
#endregion
#region 设备综合信息
/// <summary>
/// 读取PLC通用信息
/// </summary>
internal void ReadPLCComm()
{
if (CommonMethods.plcDevices.Count != 0 && CommonMethods.plcDevices[0].IsConnected)
{
Task.Run(() =>
{
UpTotalCount(null);
});
CommonMethods.TotalQty = CommonMethods.plcDevices[0].ReadFloat("D15");//生产总数
CommonMethods.ProOKQty = CommonMethods.plcDevices[0].ReadFloat("D17");//良品总数
CommonMethods.Ppm = CommonMethods.plcDevices[0].ReadFloat("D13");
CommonMethods.JiaDL = Convert.ToDouble(CommonMethods.plcDevices[0].ReadFloat("D11").ToString("F2"));
#region 开机时间/运行时间
CommonMethods.PlcOpenAndRunTime = GetList<int>(6, 0);
JYResult<byte[]> resByte = CommonMethods.plcDevices[0].ReadValue<JYResult<byte[]>>($"D50", DataType.ArrByte, 12);
if (resByte.IsSuccess && resByte.Content.Length >= 24)
{
for (int i = 0; i < CommonMethods.PlcOpenAndRunTimeAdress.Count; i++)
{
CommonMethods.PlcOpenAndRunTime[i] = IntLib.GetIntFromByteArray(resByte.Content, i * 4, DataFormat.CDAB);//ShortLib.GetShortFromByteArray(resByte.Content, i * 4, DataFormat.CDAB);
}
}
#endregion
}
}
#endregion
#region 保存12小时产能数据
public int totalNum = 0; // 总数
public int okNum = 0; // 良品数
/// <summary>
/// 储存数据到数据库
/// </summary>
/// <param name="DataCount"></param>
public void UpTotalCount(int[] DataCount)
{
try
{
DateTime dateTime = DateTime.Now;
string strDate = dateTime.ToString("yyyy-MM-dd");
// 获取当前时间的小时和分钟
int currentHour = dateTime.Hour;
int currentMinute = dateTime.Minute;
// 根据当前分钟确定统计小时
// 如果当前时间在 X:30 之前,则统计的是前一个小时的数据(从前一个小时的30分到这个小时的29分)
// 如果当前时间在 X:30 之后,则统计的是当前小时的数据(从当前小时的30分到下个小时的29分)
int statisticalHour;
if (currentMinute < 30)
{
statisticalHour = (currentHour + 23) % 24; // 前一个小时,处理跨天的情况
// 如果是凌晨0点前的数据,需要使用前一天的日期
if (currentHour == 0)
{
strDate = dateTime.AddDays(-1).ToString("yyyy-MM-dd");
}
}
else
{
statisticalHour = currentHour;
}
// 数据有效性验证
if (statisticalHour < 0 || statisticalHour > 23)
{
throw new ArgumentException($"无效的小时值: {statisticalHour}");
}
//读取统计小时的PLC投入总数和产出数
int ProdCurrAllQty = GetToDayHourQty(statisticalHour, true);
int ProdCurrOKQty = GetToDayHourQty(statisticalHour, false);
// 确保数量为非负数
if (ProdCurrAllQty < 0 || ProdCurrOKQty < 0)
{
LogHelper.Instance.WriteError($"产量不能为负数: 投入={ProdCurrAllQty}, 产出={ProdCurrOKQty}");
//throw new ArgumentException($"产量不能为负数: 投入={ProdCurrAllQty}, 产出={ProdCurrOKQty}");
}
Hourprod mh = new Hourprod
{
FDate = strDate,
FHour = statisticalHour,
ProdIn = ProdCurrAllQty,
ProdOut = ProdCurrOKQty,
TestTime = dateTime,
Ppm = (int)CommonMethods.Ppm,
JiaDL = Convert.ToDecimal(CommonMethods.JiaDL)
};
// 尝试更新记录
int res = CommonMethods.db.UpdateSingle(
mh,
it => new Hourprod
{
ProdIn = mh.ProdIn,
ProdOut = mh.ProdOut,
TestTime = mh.TestTime,
Ppm = mh.Ppm,
JiaDL = mh.JiaDL
},
it => it.FDate == mh.FDate && it.FHour == mh.FHour
);
// 如果更新失败(记录不存在),则新增记录
if (res <= 0)
{
bool addResult = CommonMethods.db.AddReturnBool<Hourprod>(mh);
if (!addResult)
{
LogHelper.Instance.WriteError($"保存小时产能新增记录失败");
}
}
// 可以添加日志记录成功信息
// Logger.Log($"成功更新/新增产能数据: 日期={strDate}, 小时={statisticalHour}, 时段={statisticalHour}:30~{(statisticalHour + 1) % 24}:29");
}
catch (Exception ex)
{
// 这里应该添加错误日志记录
LogHelper.Instance.WriteEX($"保存小时产能数据失败: ", ex);
}
}
/// <summary>
/// 每小时产能读取处理
/// </summary>
/// <param name="hour"></param>
/// <param name="flag"></param>
/// <returns></returns>
private int GetToDayHourQty(int hour, bool flag)
{
try
{
string strAddr = "";
if (flag)//产出
{
strAddr = "D" + (1040 + hour);
}
else//良品数
{
strAddr = "D" + (1070 + hour);
}
int ProdNGQty = CommonMethods.plcDevices[0].ReadInt16(strAddr);
return ProdNGQty;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "系统异常", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
return 0;
}
#endregion
#region 5、设备状态
/// <summary>
/// 定时获取设备状态
/// </summary>
/// <param name="mt"></param>
///
int OldStatues = Convert.ToInt32(IniConfigHelper.ReadIniData("上一次设备状态", "DeviceStatus", "1", CommonMethods.strDeviceStatusPath));//退出程序前设备状态
int StatuesCount = 0;
DateTime StatuesBeginTime = DateTime.Now.AddMinutes(-6);
string strGuidAlarm = IniConfigHelper.ReadIniData("上一次Guid", "DeviceGuid", Guid.NewGuid().ToString(), CommonMethods.strDeviceStatusPath);//退出程序前Guid
bool run_AlarmMark = false;//特殊情况1标志(运行状态)
bool run_IdleMark = false;//特殊情况2标志(闲置状态)
bool isUpIdleAlarm = false;//闲置状态报警信息上传标志
List<AlarmData> idleAlarmData = new List<AlarmData>();//闲置状态的报警信息
private async void ShowStateEven_Run()
{
try
{
if (CommonMethods.plcDevices.Count > 0 && CommonMethods.plcDevices != null)
{
if (CommonMethods.plcDevices[0] != null && CommonMethods.plcDevices[0].IsConnected)
{
DateTime Date = DateTime.Now;
bool isUpload = false;
string deviceStatus = string.Empty;
string deviceStatusDesc = string.Empty;
short NewStatus = CommonMethods.plcDevices[0].ReadInt16("D10");
if (NewStatus != 100)
{
//特殊情况1:运行->报警->……->运行
if (NewStatus == 3 && OldStatues == 1) { run_AlarmMark = true; }//第一次进入报警状态
if (NewStatus == 1) { run_AlarmMark = false; }//下一次运行状态,置位
if (run_AlarmMark) { NewStatus = 3; }//报警状态后下一次运行状态期间一直是报警状态
//特殊情况2:运行->闲置->……->运行
if (NewStatus == 4 && OldStatues == 1) { run_IdleMark = true; }//第一次进入闲置状态
if (NewStatus == 1) { run_IdleMark = false; }//下一次闲置状态,置位
if (run_IdleMark) { NewStatus = 4; }//闲置状态后下一次运行状态期间一直是闲置状态
//闲置状态结束报警信息上传
if (OldStatues == 4 && NewStatus != 4 && isUpIdleAlarm)
{
_ = UpIdleDataAsync(idleAlarmData, strGuidAlarm);
}
//设备状态信号灯变量
CommonMethods.Statues = new List<bool> { NewStatus == 1, NewStatus == 2, NewStatus == 3, NewStatus == 4 };
// 状态切换时生成新的 GUID
if (NewStatus != OldStatues)
{
// 从 Alert 切换到非 Alert 状态时,上传报警信息
if (OldStatues == 3 && NewStatus != 3)
{
ShowAlarmEven_Run(strGuidAlarm);
}
// 只要状态变更,就生成新的 GUID
strGuidAlarm = Guid.NewGuid().ToString(); // 强制生成新的 GUID
isUpload = true;
//StatuesCount = 0;
OldStatues = NewStatus;
StatuesBeginTime = Convert.ToDateTime(Date);
//记录最新设备状态和Guid
//CommonMethods.UpdateConfig("MES_CONFIG", "DeviceStatus", NewStatus.ToString());// 保存设备状态到xml文件
//CommonMethods.UpdateConfig("MES_CONFIG", "Guid", strGuidAlarm);// 保存设备状态到xml文件
//CommonMethods.Configxml["MES_CONFIG"]["DeviceStatus"] = NewStatus.ToString(); // 保存设备状态到xml文件
//CommonMethods.Configxml["MES_CONFIG"]["Guid"] = strGuidAlarm; // 保存设备状态到xml文件
//CommonMethods.WriteXmlFile(CommonMethods.Configxml, CommonMethods.xmlFilePath);
IniConfigHelper.WriteIniData("上一次设备状态", "DeviceStatus", NewStatus.ToString(), CommonMethods.strDeviceStatusPath);
IniConfigHelper.WriteIniData("上一次Guid", "DeviceGuid", strGuidAlarm, CommonMethods.strDeviceStatusPath);
}
else
{
// 同种状态持续 5 分钟时上传状态信息
//StatuesCount++;
//if (StatuesCount >= 5 * 60)
//{
// isUpload = true;
// StatuesCount = 0;
//}
if (Date.Subtract(StatuesBeginTime).TotalSeconds >= 5 * 60)
{
isUpload = true;
StatuesBeginTime = DateTime.Now;
}
}
// 根据状态设置设备状态和描述
switch (NewStatus)
{
case 1:
deviceStatus = "Run";
deviceStatusDesc = "正常运行";
break;
case 2://取消
deviceStatus = "Maintain";
deviceStatusDesc = "停止运行";
break;
case 3:
deviceStatus = "Alert";
deviceStatusDesc = "故障调试";
// Alert 状态时上传报警信息
ShowAlarmEven_Run(strGuidAlarm);
break;
case 4:
deviceStatus = "Idle";
deviceStatusDesc = "闲置待机";
ShowStuesEven_Run(strGuidAlarm);
break;
}
// 上传设备状态信息
if (CommonMethods.sysConfig.MesModeSwitching && CommonMethods.mesConfig.isConnected && isUpload && (NewStatus == 1 || NewStatus == 3 || NewStatus == 4))
{
var (success, message) = await CommonMethods.hbgMes.UploadEquStatusAsync(
CommonMethods.mesConfig.DeviceStatus,
CommonMethods.mesConfig.siteCode,
CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum,
CommonMethods.mesConfig.MaterialCode,
CommonMethods.mesConfig.mesUserName,
strGuidAlarm, // RUN 状态时传空
deviceStatus,
null
);
if (success)
{
StatuesCount = 0;
}
}
}
}
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteEX(ex);
}
}
#region 触摸屏弹窗
private async void ShowStuesEven_Run(string strGuid)
{
if (CommonMethods.plcDevices[0].IsConnected)
{
string strCode = "";
string alarmName = "";
short HMIStatus = CommonMethods.plcDevices[0].ReadInt16("D40");
if (HMIStatus != 0)
{
CommonMethods.AddDataMonitorLog(0, "触发了手动HMI弹窗功能".Translated());
switch (HMIStatus) //注意:B1-D12都为闲置代码,上传代码是只需上传闲置代码
{
//case 1://取消
// strCode = "B1";
// alarmName = "计划维保";
// CommonMethods.AddDataMonitorLog(0, "B1 计划维保");
// break;
//case 2://取消
// strCode = "B2";
// alarmName = "无生产计划";
// CommonMethods.AddDataMonitorLog(0, "B2 无生产计划");
// break;
//case 3://取消
// strCode = "B3";
// alarmName = "样品、试产";
// CommonMethods.AddDataMonitorLog(0, "B3 样品、试产");
// break;
//case 4://取消
// strCode = "B4";
// alarmName = "其他计划停机";
// CommonMethods.AddDataMonitorLog(0, "B4.其他计划停机");
// break;
case 5:
strCode = "D1";
alarmName = "设备故障".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 6:
strCode = "D2";
alarmName = "设备转型".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 7:
strCode = "D3";
alarmName = "工程变更".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 8:
strCode = "D4";
alarmName = "MES停机".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 9:
strCode = "D5";
alarmName = "品质异常".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 10:
strCode = "D6";
alarmName = "配件,物料更换".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 11:
strCode = "D7";
alarmName = "来料不良".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 12:
strCode = "D8";
alarmName = "待料".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 13:
strCode = "D9";
alarmName = "品质检查".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 14:
strCode = "D10";
alarmName = "清洁点检".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 15:
strCode = "D11";
alarmName = "环境不满足".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
case 16:
strCode = "D12";
alarmName = "其它非计划停机".Translated();
CommonMethods.AddDataMonitorLog(0, $"{strCode} {alarmName}");
break;
default:
break;
}
if (CommonMethods.sysConfig.MesModeSwitching && CommonMethods.mesConfig.isConnected)
{
List<AlarmData> alarmObj = new List<AlarmData>();
AlarmData alarmData = new AlarmData();
alarmData.AlarmCode = strCode;
alarmData.StartTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
alarmData.EndTime = "";
alarmData.AlarmType = "非计划损失";
alarmData.AlarmContent = alarmName;
alarmObj.Add(alarmData);
//闲置报警上传标志
isUpIdleAlarm = true;
idleAlarmData.Add(alarmData);
//上传报警
var (success, message) = await CommonMethods.hbgMes.UploadEquAlarmAsync(CommonMethods.mesConfig.DeviceAlarm, CommonMethods.mesConfig.siteCode, CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum, CommonMethods.mesConfig.MaterialCode, CommonMethods.mesConfig.mesUserName, alarmObj, strGuid);
if (success)
CommonMethods.AddDataMonitorLog(0, "HMI手动弹窗状态已上传成功,代码".Translated() + $":{strCode}");
}
bool IsSuccess = CommonMethods.plcDevices[0].WriteInt16("D40", 0);
if (IsSuccess)
CommonMethods.AddDataMonitorLog(0, $"D40已复位0".Translated());
}
}
}
#endregion
/// <summary>
/// HMI手动弹窗结束状态上传
/// </summary>
/// <param name="idleAlarmDate"></param>
/// <param name="strGuidAlarm"></param>
/// <returns></returns>
private async Task UpIdleDataAsync(List<AlarmData> idleAlarmDate, string strGuidAlarm)
{
if (idleAlarmDate.Count == 0) { return; }
string strCode = "";
foreach (var item in idleAlarmDate)
{
item.EndTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
strCode = item.AlarmCode + ",";
}
if (CommonMethods.sysConfig.MesModeSwitching && CommonMethods.mesConfig.isConnected)
{
//上传报警
var (success, message) = await CommonMethods.hbgMes.UploadEquAlarmAsync(CommonMethods.mesConfig.DeviceAlarm, CommonMethods.mesConfig.siteCode, CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum, CommonMethods.mesConfig.MaterialCode, CommonMethods.mesConfig.mesUserName, idleAlarmDate, strGuidAlarm);
if (success)
CommonMethods.AddDataMonitorLog(0, "HMI手动弹窗结束状态已上传成功,代码".Translated() + $":{strCode.TrimEnd(',')}");
}
idleAlarmData.Clear();
isUpIdleAlarm = false;
}
#endregion
#region 6、设备报警
//报警表单数据
List<AlarmForm> listAlarmForm = null;
public void GetAlarmData()
{
//加载PLC报警文件
try
{
string strFileName = System.Environment.CurrentDirectory + "\\欧姆龙报警寄存器对应表.csv";
listAlarmForm = CSVHelper<AlarmForm>.ReadCSV<AlarmForm>(strFileName);
}
catch (Exception ex)
{
MessageBox.Show("加载报警文件失败!");
}
}
/// <summary>
/// 报警信息读取
/// </summary>
/// <param name="mt"></param>
/// <param name="resByte"></param>
List<AlarmData> listOld = new List<AlarmData>();
List<AlarmData> listNew = new List<AlarmData>();
List<AlarmStatus> listAlarmStatus = null;
private async void ShowAlarmEven_Run(string strGuidAlarm)
{
if (!CommonMethods.IsRun || !CommonMethods.plcDevices[0].IsConnected)
{
return;
}
try
{
// 1. 读取 PLC 报警信息
var alarmStatusList = ReadAlarmStatusFromPLC();
if (alarmStatusList == null || listAlarmForm == null)//true
{
return;
}
// 2. 解析报警信息并生成新的报警列表
var newAlarms = ParseAlarmStatus(alarmStatusList, strGuidAlarm);
if (newAlarms.Count > 0)
{
// 3. 检测新报警并添加到旧报警列表,上传第一次报警信息
await DetectNewAlarmsAsync(newAlarms);
// 4. 显示新报警信息
DisplayAlarmInfo(newAlarms);
}
// 5. 检测已取消的报警并处理
var canceledAlarms = DetectCanceledAlarms(newAlarms);
if (canceledAlarms.Count > 0)
{
// 6. 记录已取消的报警信息(保存本地)
LogCanceledAlarms(canceledAlarms);
// 7. 上传已取消的报警信息(报警结束上传一次)
await UploadCanceledAlarmsAsync(canceledAlarms, strGuidAlarm);
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteEX(ex);
}
}
/// <summary>
/// 从 PLC 读取报警状态
/// </summary>
private List<AlarmStatus> ReadAlarmStatusFromPLC()
{
//string Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
//JYResult<byte[]> resByte = CommonMethods.plcDevices[0].ReadValue<JYResult<byte[]>>("D10150", DataType.ArrByte, 5000);
//if (resByte.IsSuccess && resByte != null)
//{
// int startPlcAddr = 10150; // 读取起始地址
// return PLCAlarmParseHelper.OmronFinsByte2Status(startPlcAddr, resByte.Content);
//}
string Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
// 读取足够的字节覆盖所有可能的报警地址
int startAddr = 10010;
int endAddr = 10519; // 根据实际最大地址调整
int wordCount = endAddr - startAddr + 1;
int byteCount = wordCount * 2; // 每个字2字节
JYResult<byte[]> resByte = CommonMethods.plcDevices[0].ReadValue<JYResult<byte[]>>(
$"D{startAddr}", DataType.ArrByte, (ushort)byteCount);
if (resByte.IsSuccess && resByte.Content != null)
{
return PLCAlarmParseHelper.OmronFinsByte2Status(startAddr, resByte.Content);
}
return null;
}
/// <summary>
/// 解析报警状态并生成新的报警列表
/// </summary>
private List<AlarmData> ParseAlarmStatus(List<AlarmStatus> alarmStatusList, string strGuidAlarm)
{
string Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
switch (LanguageManager.LanguageCode)
{
case "en"://英语报警信息
return alarmStatusList
.Where(s => s.Status)
.Join(listAlarmForm, s => s.PLCAdress, f => f.PLCAdress, (s, f) => new AlarmData()
{
AlarmGuid = strGuidAlarm,
PLCAdress = s.PLCAdress,
AlarmCode = f.AlarmCode,
AlarmContent = f.EnAlarmContent == "" ? s.PLCAdress : f.EnAlarmContent,
AlarmType = "停机报警",
AlarmDesc = f.EnAlarmContent == "" ? s.PLCAdress : f.EnAlarmContent,
AlarmState = "1",
StartTime = Date,
EndTime = "",//Date
Flag = 0
}).ToList();
case "hu"://匈牙利语报警信息
return alarmStatusList
.Where(s => s.Status)
.Join(listAlarmForm, s => s.PLCAdress, f => f.PLCAdress, (s, f) => new AlarmData()
{
AlarmGuid = strGuidAlarm,
PLCAdress = s.PLCAdress,
AlarmCode = f.AlarmCode,
AlarmContent = f.HuAlarmContent == "" ? s.PLCAdress : f.HuAlarmContent,
AlarmType = "停机报警",
AlarmDesc = f.HuAlarmContent == "" ? s.PLCAdress : f.HuAlarmContent,
AlarmState = "1",
StartTime = Date,
EndTime = "",//Date
Flag = 0
}).ToList();
case "ms"://马来语报警信息
return alarmStatusList
.Where(s => s.Status)
.Join(listAlarmForm, s => s.PLCAdress, f => f.PLCAdress, (s, f) => new AlarmData()
{
AlarmGuid = strGuidAlarm,
PLCAdress = s.PLCAdress,
AlarmCode = f.AlarmCode,
AlarmContent = f.MsAlarmContent == "" ? s.PLCAdress : f.MsAlarmContent,
AlarmType = "停机报警",
AlarmDesc = f.MsAlarmContent == "" ? s.PLCAdress : f.MsAlarmContent,
AlarmState = "1",
StartTime = Date,
EndTime = "",//Date
Flag = 0
}).ToList();
case "zh"://中语报警信息
default:
return alarmStatusList
.Where(s => s.Status)
.Join(listAlarmForm, s => s.PLCAdress, f => f.PLCAdress, (s, f) => new AlarmData()
{
AlarmGuid = strGuidAlarm,
PLCAdress = s.PLCAdress,
AlarmCode = f.AlarmCode,
AlarmContent = f.AlarmContent == "" ? s.PLCAdress : f.AlarmContent,
AlarmType = "停机报警",
AlarmDesc = f.AlarmContent == "" ? s.PLCAdress : f.AlarmContent,
AlarmState = "1",
StartTime = Date,
EndTime = "",//Date
Flag = 0
}).ToList();
}
}
/// <summary>
/// 检测新报警并添加到旧报警列表
/// </summary>
private async Task DetectNewAlarmsAsync(List<AlarmData> newAlarms)
{
foreach (var alarm in newAlarms)
{
if (!listOld.Any(o => o.PLCAdress == alarm.PLCAdress))
{
listOld.Add(alarm);
// 4.5 新增逻辑(报警开始上传一次)
await UploadCanceledAlarmsAsync(newAlarms, strGuidAlarm);
}
}
}
/// <summary>
/// 显示报警信息
/// </summary>
private void DisplayAlarmInfo(List<AlarmData> newAlarms)
{
StringBuilder sb = new StringBuilder();
newAlarms.ForEach(r => sb.Append($"{r.AlarmContent} "));
if (sb.Length > 0)
{
CommonMethods.SlideAlarmText = sb.ToString().Substring(0, sb.Length - 10);
}
}
/// <summary>
/// 检测已取消的报警
/// </summary>
private List<AlarmData> DetectCanceledAlarms(List<AlarmData> newAlarms)
{
var canceledAlarms = new List<AlarmData>();
var listOldAlarm = new List<AlarmData>(listOld);
foreach (var item in listOldAlarm)
{
if (!newAlarms.Any(n => n.PLCAdress == item.PLCAdress))
{
item.EndTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
canceledAlarms.Add(item);
listOld.Remove(item);
}
}
return canceledAlarms;
}
/// <summary>
/// 记录已取消的报警信息
/// </summary>
private void LogCanceledAlarms(List<AlarmData> canceledAlarms)
{
var nowDate = DateTime.Now;
string basePath = $@"{CommonMethods.strAlarmLogspath}\{nowDate.Year}\{nowDate.Month}";
string fileName = $@"{nowDate.Day}.csv";
string title = "GUID,报警地址,故障代码,报警内容,开始时间,结束时间,是否停机";
string alarmContent = string.Join("\n", canceledAlarms.Select(m1 =>
$"{m1.AlarmGuid},{m1.PLCAdress},{m1.AlarmCode},{m1.AlarmContent},{m1.StartTime},{m1.EndTime},{m1.AlarmType}"));
CSVHelper<AlarmCSVData>.WriterCSV(basePath, fileName, title, alarmContent);
foreach (var m1 in canceledAlarms)
{
var alarm = new SysLog
{
InsertTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
LogType = "报警日志",
Note = m1.AlarmContent == "" ? m1.PLCAdress : m1.AlarmContent,
AlarmType = "错误",
Operater = CommonMethods.currentAdmin.LoginName,
VarName = m1.PLCAdress,
AlarmSet = "",
AlarmValue = "1",
};
// 显示报警信息
CommonMethods.AddAlarmLog(alarm);
// 告警TOP10保存
SaveAlarmCount(canceledAlarms);
}
}
/// <summary>
/// 上传已取消的报警信息
/// </summary>
private async Task UploadCanceledAlarmsAsync(List<AlarmData> canceledAlarms, string strGuidAlarm)
{
if (CommonMethods.sysConfig.MesModeSwitching && CommonMethods.mesConfig.isConnected)
{
await CommonMethods.hbgMes.UploadEquAlarmAsync(
CommonMethods.mesConfig.DeviceAlarm,
CommonMethods.mesConfig.siteCode,
CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum,
CommonMethods.mesConfig.MaterialCode,
CommonMethods.mesConfig.mesUserName,
canceledAlarms,
strGuidAlarm
);
}
}
/// <summary>
/// 告警TOP10保存
/// </summary>
private void SaveAlarmCount(List<AlarmData> alarmDatas)
{
bool ISAdd = false;
var recordToUpdate = new AlarmCountEntry();
DateTime today = DateTime.Today;
DateTime upTime = DateTime.Now;
var listAlarmStatistics = CommonMethods.db.QueryWhereList<AlarmCountEntry>(it => Convert.ToDateTime(it.AlarmTime.ToString("yyyy-MM-dd")) == today);
List<AlarmCountEntry> fd = new List<AlarmCountEntry>();
if (listAlarmStatistics != null)
{
alarmDatas.ForEach(r =>
{
recordToUpdate = listAlarmStatistics.FirstOrDefault(x => x.PLCAdress.Trim() == r.PLCAdress);
if (recordToUpdate != null)
{
CommonMethods.db.Update<AlarmCountEntry>(it => it.AlarmCount == it.AlarmCount + 1, it => it.PLCAdress == r.PLCAdress && Convert.ToDateTime(it.AlarmTime.ToString("yyyy-MM-dd")) == today);
}
else
{
ISAdd = true;
AlarmCountEntry alarm = new AlarmCountEntry();
alarm.PLCAdress = r.PLCAdress;
alarm.AlarmContent = r.AlarmContent;
alarm.AlarmCount = 1;
alarm.Class = CommonMethods.strClass;
alarm.AlarmTime = upTime;
fd.Add(alarm);
}
});
if (ISAdd)
{
bool res = CommonMethods.db.AddReturnBool(fd);
ISAdd = false;
}
}
else
{
foreach (var m in listNew)
{
AlarmCountEntry alarm = new AlarmCountEntry();
alarm.PLCAdress = m.PLCAdress;
alarm.AlarmContent = m.AlarmContent;
alarm.AlarmCount = 1;
alarm.Class = CommonMethods.strClass;
alarm.AlarmTime = today;
fd.Add(alarm);
}
CommonMethods.db.AddReturnBool(fd);
}
GetASqlAlarmData(DateTime.Today, CommonMethods.strClass);
}
/// <summary>
/// 读取数据库报警信息
/// </summary>
/// <param name="today"></param>
/// <param name="strClass"></param>
public void GetASqlAlarmData(DateTime today, string strClass)
{
try
{
List<ChartDataType> chartAlarms = new List<ChartDataType>();
List<AlarmCountEntry> AlarmDatas = null;
List<string> List_X = new List<string>();
List<int> List_Y = new List<int>();
DateTime baiClassStart = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 8:30:00"));
DateTime baiClassEnd = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 20:29:59"));
DateTime yeClassStart = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 20:30:00"));
DateTime yeClassEnd = Convert.ToDateTime(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd 8:29:59"));
if (strClass == "全天".Translated())
{
AlarmDatas = CommonMethods.db.QueryList<AlarmCountEntry>("AlarmCount", 10, it => Convert.ToDateTime(it.AlarmTime.ToString("yyyy-MM-dd")) == today);
}
else if (strClass == "白班".Translated())
{
AlarmDatas = CommonMethods.db.QueryList<AlarmCountEntry>("AlarmCount", 10, it => it.AlarmTime >= baiClassStart && it.AlarmTime <= baiClassEnd && it.Class == strClass);
}
else
{
AlarmDatas = CommonMethods.db.QueryList<AlarmCountEntry>("AlarmCount", 10, it => it.AlarmTime >= yeClassStart && it.AlarmTime <= yeClassEnd && it.Class == strClass);
}
if (AlarmDatas != null && AlarmDatas.Count > 0)
{
for (int i = 0; i < AlarmDatas.Count; i++)
{
chartAlarms.Add(new ChartDataType()
{
DataType = AlarmDatas[i].AlarmContent,
DataCount = (int)AlarmDatas[i].AlarmCount
});
}
CommonMethods.Lst_AlarmTop10_Qty = chartAlarms;
}
else
{
CommonMethods.Lst_AlarmTop10_Qty = null;
}
}
catch (Exception)
{
}
}
#endregion
#region 下发权限给PLC
/// <summary>
/// 下发权限到PLC
/// </summary>
///
private async Task SendRightToPLCAsync()
{
ushort quanxian = 1;
switch (CommonMethods.currentAdmin.LoginLevel)
{
case "操作员":
quanxian = 1;
break;
case "工程师":
quanxian = 2;
break;
case "管理员":
quanxian = 3;
break;
default:
break;
}
foreach (var plc in CommonMethods.plcDevices)
{
if (plcDeviceMapping.TryGetValue(plc, out var plcDevices))
{
foreach (var plcDevice in plcDevices)
{
if (plcDevice.IsConnected)
{
JYResult result = await Task.Run(() => plc.WriteValue("D21", (short)quanxian, DataType.Short));
if (result.IsSuccess)
{
CommonMethods.AddDataMonitorLog(1, $"Write_PLC_permission_Successful");
}
else
{
CommonMethods.AddDataMonitorLog(2, $"Write_PLC_permission(D15)_Failed");
}
}
}
}
}
}
#endregion
#region 获取白夜班次
bool qiehuan = false;
private void SetClass()
{
try
{
var nowDate = DateTime.Now;
string strClass = "";
DateTime time1 = Convert.ToDateTime($"{nowDate.ToShortDateString()} {CommonMethods.ChangeClassTime}");
DateTime time2 = Convert.ToDateTime($"{nowDate.ToShortDateString()} {Convert.ToDateTime(CommonMethods.ChangeClassTime).AddHours(12).ToString("HH:mm:ss")}");
if (nowDate <= time1 && nowDate >= time2)
{
if (CommonMethods.plcDevices[0].IsConnected)
{
if (!qiehuan)
{
if (!SetPlcTime())
{
CommonMethods.AddLog(true, $"PC写入PLC时间失败".Translated());
}
else
{
qiehuan = true;
CommonMethods.AddLog(false, "PC写入PLC时间成功".Translated());
}
}
}
strClass = "白班".Translated();
}
else
{
qiehuan = false;
strClass = "夜班".Translated();
}
CommonMethods.strClass = strClass;
}
catch (Exception ex)
{
CommonMethods.AddLog(true, "PC写入PLC时间异常".Translated() + ":" + ex.Message);
LogHelper.Instance.WriteEX(ex);
}
}
#endregion
#region 写入PLC时间
/// <summary>
/// 写当前时间给PLC
/// </summary>
private bool SetPlcTime()
{
var nowDate = DateTime.Now;
string yearStr = nowDate.Year.ToString();
bool IsSuccess = false;
// 为防止PLC时间出现偏差,每次自动运行的时候写PLC系统时间
short[] writeArr = new short[]
{
Convert.ToInt16(nowDate.Year),//D70
Convert.ToInt16(nowDate.Month),//D71
Convert.ToInt16(nowDate.Day),//D72
Convert.ToInt16(nowDate.Hour),//D73
Convert.ToInt16(nowDate.Minute),//D74
Convert.ToInt16(nowDate.Second),//D75
(short)1 //D76
};
if (CommonMethods.plcDevices[0].IsConnected)
{
JYResult result = CommonMethods.plcDevices[0].WriteValue("D70", writeArr, DataType.ArrShort);
if (result.IsSuccess)
{
IsSuccess = true;
}
}
return IsSuccess;
}
#region 9、IDisposable 实现
// 如果需要在StopAsync中也显示进度,可以添加一个进度回调
private Task processTask;
public async Task StopAsync(IProgress<string> progress = null)
{
_cts.Cancel();
if (_heartbeatTask != null)
{
progress?.Report("正在停止心跳任务...");
try
{
await _heartbeatTask;
}
catch (OperationCanceledException)
{
// 忽略取消异常
}
}
if (processTask != null)
{
progress?.Report("正在停止处理任务...");
try
{
await processTask;
}
catch (OperationCanceledException)
{
// 忽略取消异常
}
}
foreach (var pair in groupTasks)
{
progress?.Report($"正在停止组任务: {pair.Key}...");
try
{
await pair.Value;
}
catch (OperationCanceledException)
{
// 忽略取消异常
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_cts?.Cancel();
_cts?.Dispose();
processTask?.Wait();
// 断开所有设备连接
foreach (var plc in CommonMethods.plcDevices)
{
plc.Disconnect();
//if (plcAlarmReaders.TryGetValue(plc, out var alarmReader))
//{
// alarmReader.PLCAlarm_New -= AlrmReader_PLCAlarm_New;
// alarmReader.PLCAlarm_Remove -= AlrmReader_PLCAlarm_Remove;
//}
}
CommonMethods.plcDevices.Clear();
plcDeviceMapping.Clear();
plcAlarmReaders.Clear();
CommonMethods.plcStatuses.Clear();
DisconnectAllPlcs();
}
}
#endregion
#endregion
#region 7、辅助方法
private static PLCType GetPLCTypeEnum(string plcTypeString)
{
if (Enum.TryParse(plcTypeString, out PLCType plcType))
{
return plcType;
}
throw new ArgumentException($"PLC 类型无效: {plcTypeString}");
}
#endregion
}
}