报警插件开发
# 开发指南
介绍报警插件的开发
# 概述
概述
报警插件是IoTCenter提供的对外报警方式,是由测点触发,比如短信报警、邮件报警、声光报警等。报警插件配置方式如下:
字段含义:
字段 | 说明 |
---|---|
Proc_Code | 模块编码,按位编码,前两位已经被系统占用(1和2),编码只能是4、8、16... |
Proc_Module | 驱动文件,报警模块dll的名称 |
Proc_name | 驱动名称,可自定义 |
Proc_parm | 参数配置:自行定义,在报警驱动中解析 |
Comment | 备注说明 |
平台应用商店中上架了三种报警插件可使用,可在设备管理中编辑设备来配置:
# 开发框架
接下来以Email报警模块的代码为例,讲解开发过程:
using GWDataCenter;
using GWDataCenter.Database;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Threading;
namespace AlarmCenter.AlarmHandle.GWEmailPro
{
public class CAlarm : CAlarmEquipBase
{
const int ThreadInterval = 10*1000;//间隔10秒
const int RetryTimes = 3;//报警失败重复次数
Thread AlarmScanThread;
SendEmail MyEmail = null;
string strParm;
string ModuleNm;
string MailAddress, PassWord, Title, SendNm, ReceiveNm, MailServer, SMPTPort,LocalPort;
CAlarmItem CurrentAlarmItem = null;
public void StartAlarmThread()
{
ThreadStart entryPoint = new ThreadStart(AlarmScan);
AlarmScanThread = new Thread(entryPoint);
AlarmScanThread.IsBackground = true;
AlarmScanThread.Start();
}
public void AlarmScan()
{
while (true)
{
if (CurrentAlarmItem != null)
{
DoAlarm(CurrentAlarmItem);
CurrentAlarmItem = null;
}
lock (AlarmItemQueue)
{
if (AlarmItemQueue.Count > 0)
{
CurrentAlarmItem = (CAlarmItem)AlarmItemQueue.Dequeue();
}
}
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckRise));
Thread.Sleep(500);
}
}
//每隔一秒钟检查延时报警列表
void CheckRise(object o)
{
Thread.Sleep(1000);
CheckAlarmRise();
}
public override bool init(AlarmProcTableRow Row)
{
try
{
if (MyEmail == null)
MyEmail = new SendEmail();
strParm = Row.Proc_parm;
ModuleNm = Row.Proc_name;
if (strParm.IndexOf(';') > 0)
{
MailAddress = strParm.Split(';')[0];
if (MailAddress.IndexOf('=') > 0)
{
MailAddress = MailAddress.Split('=')[1].Trim();
}
PassWord = strParm.Split(';')[1];
if (PassWord.IndexOf('=') > 0)
{
PassWord = PassWord.Split('=')[1].Trim();
}
Title = strParm.Split(';')[2];
if (Title.IndexOf('=') > 0)
{
Title = Title.Split('=')[1].Trim();
}
SendNm = strParm.Split(';')[3];
if (SendNm.IndexOf('=') > 0)
{
SendNm = SendNm.Split('=')[1].Trim();
}
ReceiveNm = strParm.Split(';')[4];
if (ReceiveNm.IndexOf('=') > 0)
{
ReceiveNm = ReceiveNm.Split('=')[1].Trim();
}
MailServer = strParm.Split(';')[5];
if (MailServer.IndexOf('=') > 0)
{
MailServer = MailServer.Split('=')[1].Trim();
}
SMPTPort = strParm.Split(';')[6];
if (SMPTPort.IndexOf('=') > 0)
{
SMPTPort = SMPTPort.Split('=')[1].Trim();
}
LocalPort = strParm.Split(';')[7];
if (LocalPort.IndexOf('=') > 0)
{
LocalPort = LocalPort.Split('=')[1].Trim();
}
StartAlarmThread();
}
}
catch (Exception e)
{
MessageService.AddMessage(MessageLevel.Fatal, General.GetExceptionInfo(e), 0, false);
}
return true;
}
public override bool DoAlarm(CAlarmItem item)
{
List<AdministratorTableRow> dt = item.GetAdminsOfAlarm(item.Equipno, item.Time);//得到报警人员列表
foreach (AdministratorTableRow r in dt)
{
try
{
int level;
level = r.AckLevel.GetType() == typeof(System.DBNull) ? 0 : r.AckLevel;
if (level == item.AlarmLevel)//同一级别才报警
{
string Content = item.Time.ToString("HH:mm:ss ") + item.Event;
int iPort = Convert.ToInt32(SMPTPort);
int iLocalPort = Convert.ToInt32(LocalPort);
//数据库里面的Email地址已经加密,需要解密
string strEmail = GWDataCenter.DataCenter.MD5Decrypt(r.EMail, GWDataCenter.DataCenter.GeneratMD5Key());
if (!MyEmail.Init(MailAddress, PassWord, strEmail, Title, ReceiveNm, SendNm, MailServer, iPort, iLocalPort))
{
string msg1 = string.Format("{0}:{1}--{2}--{3}", strEmail,
ResourceService.GetString("AlarmCenter.Email.Msg1", "发送Email报警"),
item.Event,
ResourceService.GetString("AlarmCenter.Email.Msg3", "失败!"));
MessageService.AddMessage(MessageLevel.Info, msg1, 0, false);
lock (GWDbProvider.lockstate)
{
using (var db = StationItem.MyGWDbProvider.serviceProvider.GetService<GWDataContext>())
{
var Row = new AlarmRecTableRow
{
proc_name = ModuleNm,
Administrator = r.Administrator,
gwEvent = item.Event,
time = General.Convert2DT(DateTime.Now),
comment = ResourceService.GetString("AlarmCenter.Email.Msg3")
};
db.AlarmRecTable.Add(Row);
db.SaveChanges();
}
}
continue;
}
MyEmail.SendBodyTxt(Content);//设置发邮件内容
MyEmail.Send();
string msg = string.Format("{0}:{1}--{2}--{3}", strEmail,
ResourceService.GetString("AlarmCenter.Email.Msg1", "发送Email报警"),
item.Event,
ResourceService.GetString("AlarmCenter.Email.Msg2", "成功!"));
MessageService.AddMessage(MessageLevel.Info, msg, 0);
lock (GWDbProvider.lockstate)
{
using (var db = StationItem.MyGWDbProvider.serviceProvider.GetService<GWDataContext>())
{
var Row = new AlarmRecTableRow
{
proc_name = ModuleNm,
Administrator = r.Administrator,
gwEvent = item.Event,
time = General.Convert2DT(DateTime.Now),
comment = "报警成功"
};
db.AlarmRecTable.Add(Row);
db.SaveChanges();
}
}
}
}
catch (Exception e)
{
GWDataCenter.DataCenter.WriteLogFile(General.GetExceptionInfo(e));
Thread.Sleep(5000);
MyEmail = null;
MyEmail = new SendEmail();
//MessageService.AddMessage(MessageLevel.Warn, General.GetExceptionInfo(e), 0, false);
}
}
return true;
}
}
}
public class CAlarm : CAlarmEquipBase
报警模块的类名必须是CAlarm
,从 CAlarmEquipBase
类派生。CAlarmEquipBase
类包含在GWDataCenter
包中,GWDataCenter
是IoTCenter的核心库。看看Email报警模块的依赖包:
IoTCenter启动时,会加载配置好的报警插件,自动调用public override bool init(AlarmProcTableRow Row)
。AlarmProcTableRow Row
就是配置行的实体对象。init
函数主要作用是解析数据库中配置的参数,同时启动一个报警扫描线程:StartAlarmThread()
。
重点讲解一下报警扫描函数:
报警扫描函数代码
public void AlarmScan()
{
while (true)
{
if (CurrentAlarmItem != null)
{
DoAlarm(CurrentAlarmItem);
CurrentAlarmItem = null;
}
lock (AlarmItemQueue)
{
if (AlarmItemQueue.Count > 0)
{
CurrentAlarmItem = (CAlarmItem)AlarmItemQueue.Dequeue();
}
}
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckRise));
Thread.Sleep(500);
}
}
AlarmItemQueue
是CAlarmEquipBase
中的报警项CAlarmItem
队列,队列如果存在报警项,就会调用处理函数DoAlarm(CurrentAlarmItem)
。同时AlarmScan
还会调用CheckRise
,用于检查报警升级周期。
我们看看CAlarmItem
的字段定义:
CAlarmItem
的字段定义
public class CAlarmItem
{
public int Equipno;//报警设备号
public string Type;//报警类型:EQUIP报警为"E",YCP报警为"C",YXP报警为"X"
public int YcYxNo;//测点号
public string Event;//报警事件描述
public string Wave;//报警对应的声音文件
public DateTime Time;//报警发生的时间
public int Retrys;//重试次数
public bool IsAlarm;//报警状态:true--报警;false--不报警
public bool IsSendAlarm;//是否发送报警
public int? AlarmRiseCycle;//报警升级周期:分钟
public int AlarmLevel=0;//报警通知级别:报警排班中不同人员对应不同级别,这个级别与报警升级周期关联,定时升级
}
CheckRise
就是根据 CAlarmItem
中的 AlarmRiseCycle
,获取当前 CAlarmItem
中的 AlarmLevel
。
在人员的报警排班中,有对应的报警级别设置:
报警通知级别的作用是用于分级报警,比如测点A发生报警,如果AlarmRiseCycle
设为15,即报警升级周期是15分钟。当A发生报警后,如果在15分钟内没有恢复正常,报警通知级别升为1;如果30分钟内还没有得到处理,则报警通知级别升为2。这样,我们可以把某个维护人员的通知级别设为0,把他的上级领导设为1,把他领导的领导设为2,这样就实现了报警的分级通知管理。
public override bool DoAlarm(CAlarmItem item)
是处理当前报警的函数,说明如下:
List<AdministratorTableRow> dt = item.GetAdminsOfAlarm(item.Equipno, item.Time);
得到有权限接收当前报警的人员列表
只有同一通知级别才报警:
level = r.AckLevel.GetType() == typeof(System.DBNull) ? 0 : r.AckLevel;
if (level == item.AlarmLevel)//同一级别才报警
{
//发送报警...
}
以下代码块是保存报警处理事件到数据库AlarmRec表的标准操作:
代码
lock (GWDbProvider.lockstate)
{
using (var db = StationItem.MyGWDbProvider.serviceProvider.GetService<GWDataContext>())
{
var Row = new AlarmRecTableRow
{
proc_name = ModuleNm,
Administrator = r.Administrator,
gwEvent = item.Event,
time = General.Convert2DT(DateTime.Now),
comment = ResourceService.GetString("报警成功")
};
db.AlarmRecTable.Add(Row);
db.SaveChanges();
}
continue;
}
注意
获取报警方式
显示报警:var result = (alarm_scheme & 1) == 1 ? 1 : 0;
记录报警:var result = (alarm_scheme & 2) == 2 ? 1 : 0;
短信报警:var result = (alarm_scheme & 4) == 4 ? 1 : 0;
Email报警:var result = (alarm_scheme & 8) == 8 ? 1 : 0;
工单报警:var result = (alarm_scheme & 16) == 16 ? 1 :0;
当result 为1时说明支持当前属性。注:Equip(设备表)、YCP(遥测表)、YXP(遥信表)均存在alarm_scheme字段。
具体怎么实现的邮件报警不再阐述,开发者熟悉框架结构就可以开发出各种报警模块了。