添加项目文件。
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace PLC.PLCAlarm
|
||||
{
|
||||
/// <summary>
|
||||
/// 从文件读取的PLC报警信息
|
||||
/// </summary>
|
||||
public class AlarmFileData
|
||||
{
|
||||
/// <summary>
|
||||
/// alarmData有5个元素,分别对应字,位,报警触发值,报警代码,报警信息
|
||||
/// </summary>
|
||||
/// <param name="alarmData"></param>
|
||||
public AlarmFileData(string[] alarmData)
|
||||
{
|
||||
Word = int.Parse(alarmData[0]);
|
||||
Bit = int.Parse(alarmData[1]);
|
||||
AlarmTriger = alarmData[2] == "1";
|
||||
AlarmCode = alarmData[3];
|
||||
AlarmInfo = alarmData[4];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 报警字地址
|
||||
/// </summary>
|
||||
public int Word { get; set; }
|
||||
/// <summary>
|
||||
/// 报警位地址
|
||||
/// </summary>
|
||||
public int Bit { get; set; }
|
||||
/// <summary>
|
||||
/// 报警触发值
|
||||
/// (为true的话就是当读取值是true的时候触发这个报警)
|
||||
/// </summary>
|
||||
public bool AlarmTriger { get; set; }
|
||||
/// <summary>
|
||||
/// 要上传到mes的报警代码
|
||||
/// </summary>
|
||||
public string AlarmCode { get; set; }
|
||||
/// <summary>
|
||||
/// 报警说明
|
||||
/// </summary>
|
||||
public string AlarmInfo { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PLC.PLCAlarm
|
||||
{
|
||||
/// <summary>
|
||||
/// 报警信息管理器
|
||||
/// </summary>
|
||||
public class AlarmManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, AlarmEntry> activeAlarms = new ConcurrentDictionary<string, AlarmEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// 添加报警
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
public void AddAlarm(RecordAlarmDataArgs e)
|
||||
{
|
||||
var alarmInfo = new AlarmEntry
|
||||
{
|
||||
AlarmCode = e.AlarmCode,
|
||||
AlarmInfo = e.AlarmInfo,
|
||||
AlarmDate = e.AlarmDate,
|
||||
StartTime = e.StartTime
|
||||
};
|
||||
|
||||
activeAlarms.TryAdd(e.AlarmCode, alarmInfo);
|
||||
}
|
||||
/// <summary>
|
||||
/// 删除报警
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
public void RemoveAlarm(RecordAlarmDataArgs e)
|
||||
{
|
||||
activeAlarms.TryRemove(e.AlarmCode, out _);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取报警文本
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetFormattedAlarmText()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var alarm in activeAlarms.Values.OrderByDescending(a => a.StartTime))
|
||||
{
|
||||
sb.AppendLine($"报警: 代码 {alarm.AlarmCode}, 内容 {alarm.AlarmInfo}, 日期 {alarm.AlarmDate}, 开始时间 {alarm.StartTime:HH:mm:ss}");
|
||||
}
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取报警JOSN
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetActiveAlarmsJson()
|
||||
{
|
||||
return JsonConvert.SerializeObject(activeAlarms.Values);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 报警类
|
||||
/// </summary>
|
||||
public class AlarmEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 报警代码
|
||||
/// </summary>
|
||||
public string AlarmCode { get; set; }
|
||||
/// <summary>
|
||||
/// 报警信息
|
||||
/// </summary>
|
||||
public string AlarmInfo { get; set; }
|
||||
/// <summary>
|
||||
/// 报警时间
|
||||
/// </summary>
|
||||
public string AlarmDate { get; set; }
|
||||
/// <summary>
|
||||
/// 报警开始时间
|
||||
/// </summary>
|
||||
public DateTime StartTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
using JinYuan.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace PLC.PLCAlarm
|
||||
{
|
||||
/// <summary>
|
||||
/// PLC报警记录模块
|
||||
/// </summary>
|
||||
public class PLCAlarmReader
|
||||
{
|
||||
/// <summary>
|
||||
/// 报警模板(从触摸屏导出的报警数据)
|
||||
/// </summary>
|
||||
private readonly List<AlarmFileData> alarmFileDataList;
|
||||
/// <summary>
|
||||
/// 报警记录路径(实际记录下来的实时报警)
|
||||
/// </summary>
|
||||
private readonly string alarmRecordPath;
|
||||
/// <summary>
|
||||
/// 是否忽略报警状态下的新增报警
|
||||
/// </summary>
|
||||
private readonly bool ignoreAlarm;
|
||||
/// <summary>
|
||||
/// 报警记录类型
|
||||
/// </summary>
|
||||
private readonly RecordType recordType;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个报警读取器
|
||||
/// </summary>
|
||||
/// <param name="AlarmFilePath">报警文件路径
|
||||
/// <para>
|
||||
/// 文件格式 .csv
|
||||
/// <para>字,位,触发数值,报警代码,报警内容</para>
|
||||
/// <para>40000,0,1,E001,急停报警(IO:A1_I0.05)</para>
|
||||
/// <para>40000,1,1,E002,上下料安全门报警(IO:A1_I0.06)</para>
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="AlarmRecordPath">报警记录目录路径</param>
|
||||
/// <param name="recType">报警记录类型</param>
|
||||
/// <param name="ignore">是否忽略报警状态下的新增报警</param>
|
||||
public PLCAlarmReader(string AlarmFilePath, string AlarmRecordPath, RecordType recType = RecordType.RecordWhenStart, bool ignore = false)
|
||||
{
|
||||
alarmFileDataList = ReadAlarmFile(AlarmFilePath);
|
||||
alarmRecordPath = AlarmRecordPath;
|
||||
recordType = recType;
|
||||
ignoreAlarm = ignore;
|
||||
RecordAlarms = new List<RecordAlarmDataArgs>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增报警事件
|
||||
/// </summary>
|
||||
public event EventHandler<RecordAlarmDataArgs> PLCAlarm_New;
|
||||
/// <summary>
|
||||
/// 报警复位事件
|
||||
/// </summary>
|
||||
public event EventHandler<RecordAlarmDataArgs> PLCAlarm_Remove;
|
||||
/// <summary>
|
||||
/// 当前记录中的报警
|
||||
/// </summary>
|
||||
public List<RecordAlarmDataArgs> RecordAlarms { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 读取本地报警文件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private List<AlarmFileData> ReadAlarmFile(string alarmFilePath)
|
||||
{
|
||||
List<AlarmFileData> fileAlarmList = new List<AlarmFileData>();
|
||||
try
|
||||
{
|
||||
if (!File.Exists(alarmFilePath))
|
||||
return null;
|
||||
using (StreamReader sr = new StreamReader(alarmFilePath, Encoding.Default))
|
||||
{
|
||||
if (!sr.EndOfStream)
|
||||
sr.ReadLine(); // 读掉第一行标题行
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string[] alarmData = sr.ReadLine().Split('\t');
|
||||
fileAlarmList.Add(new AlarmFileData(alarmData));
|
||||
}
|
||||
}
|
||||
return fileAlarmList;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Instance.WriteError("读取本地报警信息配置文件出错:" + ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 寻找报警代码
|
||||
/// </summary>
|
||||
/// <param name="word">字地址</param>
|
||||
/// <param name="bit">位地址</param>
|
||||
/// <param name="value">值</param>
|
||||
/// <param name="alarmData">报警信息</param>
|
||||
/// <returns>是否找到</returns>
|
||||
private bool FindAlarm(int word, int bit, bool value, out AlarmFileData alarmData)
|
||||
{
|
||||
if (alarmFileDataList == null)
|
||||
{
|
||||
alarmData = null;
|
||||
return false;
|
||||
}
|
||||
foreach (var item in alarmFileDataList)
|
||||
{
|
||||
if (item.Word == word &&
|
||||
item.Bit == bit &&
|
||||
item.AlarmTriger == value)
|
||||
{
|
||||
alarmData = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
alarmData = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得当前报警列表
|
||||
/// </summary>
|
||||
/// <param name="startAddr">报警字开始地址</param>
|
||||
/// <param name="addrStep">报警地址递增步进(个别情况用)
|
||||
/// <para>比如按字读取基恩士MR地址时,字地址是40000,40100,40200这样增加的,此时就要把addrStep设成100</para>
|
||||
/// </param>
|
||||
/// <param name="length">报警字长度(有几个字)</param>
|
||||
/// <param name="readDatas">读到的数据</param>
|
||||
/// <param name="readPLCAlrms">返回的当前报警列表</param>
|
||||
/// <returns></returns>
|
||||
private bool GenerateAlarmList(int startAddr, int length, ushort[] readDatas, out List<AlarmFileData> readPLCAlrms, int addrStep = 1)
|
||||
{
|
||||
bool isAlarm = false;
|
||||
readPLCAlrms = new List<AlarmFileData>();
|
||||
for (int wi = 0; wi < length; wi++)
|
||||
{
|
||||
ushort word = readDatas[wi];
|
||||
if (word != 0)
|
||||
{
|
||||
isAlarm = true;
|
||||
bool[] bits = PLCDataTool.WordToBits(word);
|
||||
for (int bi = 0; bi < 16; bi++)
|
||||
{
|
||||
if (FindAlarm(startAddr + wi * addrStep, bi, bits[bi], out AlarmFileData alarmdata))
|
||||
{
|
||||
if (!readPLCAlrms.Exists((p) => p.AlarmCode == alarmdata.AlarmCode))
|
||||
readPLCAlrms.Add(alarmdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return isAlarm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输入读取的数据,判断当前是否报警,保存报警记录
|
||||
/// </summary>
|
||||
/// <param name="startAddr">报警字开始地址</param>
|
||||
/// <param name="addrStep">报警地址递增步进(个别情况用)
|
||||
/// <para>比如按字读取基恩士MR地址时,字地址是40000,40100,40200这样增加的,此时就要把addrStep设成100</para>
|
||||
/// </param>
|
||||
/// <param name="length">报警字长度(有几个字)</param>
|
||||
/// <param name="readDatas">读到的数据</param>
|
||||
/// <returns></returns>
|
||||
public bool IsPLCAlarm(int startAddr, int length, ushort[] readDatas, int addrStep = 1)
|
||||
{
|
||||
if (GenerateAlarmList(startAddr, length, readDatas, out List<AlarmFileData> readPLCAlrms, addrStep))
|
||||
{
|
||||
// 新增
|
||||
if (!ignoreAlarm || RecordAlarms.Count <= 0) //当不忽略报警状态下新增报警,或者当前是无报警状态的话,就记录
|
||||
{
|
||||
foreach (var alrmItem in readPLCAlrms)
|
||||
{
|
||||
if (RecordAlarms.Exists(p => p.AlarmCode == alrmItem.AlarmCode))
|
||||
continue;
|
||||
// 记录新增报警
|
||||
var recordData = new RecordAlarmDataArgs()
|
||||
{
|
||||
AlarmCode = alrmItem.AlarmCode,
|
||||
AlarmInfo = alrmItem.AlarmInfo,
|
||||
AlarmDate = DateTime.Now.ToString("yyyy/MM/dd"),
|
||||
StartTime = DateTime.Now
|
||||
};
|
||||
RecordAlarms.Add(recordData);
|
||||
SaveAlarmRecordWhenStart(recordData);
|
||||
OnAlarm_New(recordData);
|
||||
}
|
||||
}
|
||||
// 复位
|
||||
for (int i = RecordAlarms.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// 遍历记录中的报警,如果有当前报警中不包含的报警项,说明报警已复位
|
||||
if (!readPLCAlrms.Exists(p => p.AlarmCode == RecordAlarms[i].AlarmCode))
|
||||
{
|
||||
SaveAlarmRecordWhenFinish(RecordAlarms[i], DateTime.Now);
|
||||
OnAlarm_Remove(RecordAlarms[i]);
|
||||
RecordAlarms.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 全都复位
|
||||
for (int i = RecordAlarms.Count - 1; i >= 0; i--)
|
||||
{
|
||||
SaveAlarmRecordWhenFinish(RecordAlarms[i], DateTime.Now);
|
||||
OnAlarm_Remove(RecordAlarms[i]);
|
||||
RecordAlarms.RemoveAt(i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发新增报警事件
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
private void OnAlarm_New(RecordAlarmDataArgs args)
|
||||
{
|
||||
// 以线程安全的方式引发事件
|
||||
var temp = PLCAlarm_New;
|
||||
temp?.Invoke(this, args);
|
||||
}
|
||||
/// <summary>
|
||||
/// 触发报警复位事件
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
private void OnAlarm_Remove(RecordAlarmDataArgs args)
|
||||
{
|
||||
// 以线程安全的方式引发事件
|
||||
var temp = PLCAlarm_Remove;
|
||||
temp?.Invoke(this, args);
|
||||
}
|
||||
|
||||
|
||||
#region 保存报警记录
|
||||
/// <summary>
|
||||
/// 当报警消除的时候记录已消除的报警
|
||||
/// </summary>
|
||||
/// <param name="alrmData">报警数据</param>
|
||||
/// <param name="endTime">结束时间</param>
|
||||
private void SaveAlarmRecordWhenFinish(RecordAlarmDataArgs alrmData, DateTime endTime)
|
||||
{
|
||||
if (recordType != RecordType.RecordWhenFinish)
|
||||
return;
|
||||
|
||||
string start = alrmData.StartTime.ToString("HH:mm:ss");
|
||||
string end = endTime.ToString("HH:mm:ss");
|
||||
string totaltime = (endTime - alrmData.StartTime).TotalMinutes.ToString("f2");
|
||||
|
||||
if (!Directory.Exists(alarmRecordPath))
|
||||
{
|
||||
Directory.CreateDirectory(alarmRecordPath);
|
||||
}
|
||||
try
|
||||
{
|
||||
string filePath = Path.Combine(alarmRecordPath, DateTime.Now.ToString("yyyy-MM-dd") + ".csv");
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(filePath))
|
||||
{
|
||||
sw.WriteLine("报警信息,报警代码,统计日期,开始时间,结束时间,消耗时间(min)");
|
||||
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start + "," + end + "," + totaltime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(filePath, true))
|
||||
{
|
||||
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start + "," + end + "," + totaltime);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Instance.WriteError(ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当报警触发的时候记录报警
|
||||
/// </summary>
|
||||
/// <param name="alrmData">报警数据</param>
|
||||
private void SaveAlarmRecordWhenStart(RecordAlarmDataArgs alrmData)
|
||||
{
|
||||
if (recordType != RecordType.RecordWhenStart)
|
||||
return;
|
||||
|
||||
string start = alrmData.StartTime.ToString("HH:mm:ss");
|
||||
|
||||
if (!Directory.Exists(alarmRecordPath))
|
||||
{
|
||||
Directory.CreateDirectory(alarmRecordPath);
|
||||
}
|
||||
try
|
||||
{
|
||||
string filePath = Path.Combine(alarmRecordPath, DateTime.Now.ToString("yyyy-MM-dd") + ".csv");
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(filePath))
|
||||
{
|
||||
sw.WriteLine("报警信息,报警代码,统计日期,开始时间");
|
||||
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(filePath, true))
|
||||
{
|
||||
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Instance.WriteError(ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace PLC.PLCAlarm
|
||||
{
|
||||
/// <summary>
|
||||
/// 报警记录数据(同时作为报警事件的事件参数)
|
||||
/// </summary>
|
||||
public class RecordAlarmDataArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 报警信息
|
||||
/// </summary>
|
||||
public string AlarmInfo { get; set; }
|
||||
/// <summary>
|
||||
/// 报警代码
|
||||
/// </summary>
|
||||
public string AlarmCode { get; set; }
|
||||
/// <summary>
|
||||
/// 报警日期
|
||||
/// </summary>
|
||||
public string AlarmDate { get; set; }
|
||||
/// <summary>
|
||||
/// 报警开始时间
|
||||
/// </summary>
|
||||
public DateTime StartTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace PLC.PLCAlarm
|
||||
{
|
||||
/// <summary>
|
||||
/// 报警文件记录选项
|
||||
/// </summary>
|
||||
public enum RecordType
|
||||
{
|
||||
/// <summary>
|
||||
/// 不记录
|
||||
/// </summary>
|
||||
NoRecord,
|
||||
/// <summary>
|
||||
/// 报警触发的时候记录
|
||||
/// </summary>
|
||||
RecordWhenStart,
|
||||
/// <summary>
|
||||
/// 报警复位的时候记录
|
||||
/// </summary>
|
||||
RecordWhenFinish
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user