一个可扩展的报警系统Quick-Alarm

本文介绍了一个通用报警框架的设计思路,支持多种报警方式的自由选择与动态配置,以及用户自定义报警规则拓展。该框架旨在简化报警操作,提高报警效率。

背景

日常的系统中,报警是不可缺少的一环,目前报警方式很多,最常见的有直接打日志,微信报警,短信报警,邮件报警等;而涉及到报警,一般不可避免的需要提前设置一些基本信息,如报警方式,报警频率,报警用户,开关等;

另外一个常见的问题是一般采用的是单一的报警方式,比如不管什么类型的报警全部都用短信方式触达,然后就会发现手机时常处于被淹没的状态了,久而久之对报警短信就不会敏感了

目标

因此我们准备设计一个通用的报警框架

  • 可以自由选择报警方式,
  • 支持用户自定义报警方式拓展
  • 支持动态的报警配置,
  • 支持用户自定义报警规则拓展
  • 支持报警方式自动切换规则设定
  • 支持报警方式自定义自动切换规则拓展

设计

整体来说,报警主要可以划分为三个步骤,如下:

  • 提交报警:对外部使用者提供的接口
  • 选择报警:根据报警相关信息,选择具体的报警执行单元
  • 执行报警:实现具体的报警逻辑

从任务划分上来看,比较清晰简单,但是每一块的内容又必须可以拓展,

  • 选择报警:

    • 报警规则的制定
    • 报警规则加载器 ConfLoader
    • 报警规则变更的触发器 ConfChangeTrigger
    • 报警规则解析器
      • ConfParse : 解析文本格式报警规则为业务对象
      • AlarmSelector :根据报警规则和报警类型,选择具体报警执行器 AlarmExecute
  • 执行报警:

    • 线程池执行(以防止影响主业务流程)
    • AlarmExecute的动态拓展(支持用户自定义的报警器实现)
    • 实际的报警逻辑

根据上面的拆解,在应用启动的时候,就有一些事情必须去做了

  1. ConfLoader的选择
  2. 报警规则加载
  3. AlarmExecute的加载(包括默认的+自定义实现的)

下图显示在应用启动时,报警规则解析的相关步骤

至于报警执行器的加载就比较简单了,如下图

因此,整个的工作流程如下图

任务拆解

通过前面的任务设计之后,对需要做的东西有了一个大概的脉络了,因此在正式操刀实现之前,下对整个架构进行任务拆解,看下可以具体的执行步骤可以怎么来

  • 最直接的就是设计报警执行器AlarmExecute
    • 定义基本接口
    • 制定自定义扩展规则
  • 接下来就是设计报警规则
    • 如何加载报警规则?
    • 报警规则具体的定义细则
    • 报警规则的解析:即根据报警类型来获取报警执行器
    • 报警规则动态更新支持
  • 报警线程池
    • 维护报警队列
    • 报警的计数与频率控制
  • 封装对外使用接口

所以,通过上面的分析可以看出,这个系统的结构还是蛮简单的,整个只需要四个部分就可以搞定,其中最主要的就是前面两个了,后面将分别说明

小结

做一个东西,当然是希望可以带来一些用处,或者能学习到什么东西,才不枉花费精力来折腾一下,那么我们这个报警系统,究竟有什么用,或者可以从中学习到什么东西呢?

用途:

  • 支持灵活可配的报警规则,以及具体报警业务的自定义拓展
  • 目标就是统一报警的使用姿势,也就是不管什么报警,都一个姿势,但是内部可以玩出各种花样,对使用者而言就方便简洁了

学习:

抛开特有的知识点,可以抽象一些公共可用的地方,大概就下面这两点了

  • 我们可以如何支持功能的动态可拓展
  • 线程池的使用

IV. 其他

相关博文

  1. 报警系统QuickAlarm总纲
  2. 报警系统QuickAlarm之报警执行器的设计与实现
  3. 报警系统QuickAlarm之报警规则的设定与加载
  4. 报警系统QuickAlarm之报警规则解析
  5. 报警系统QuickAlarm之频率统计及接口封装
  6. 报警系统QuickAlarm使用手册

项目

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见解不全,如有问题,欢迎批评指正

扫描关注,java分享

QuickAlarm直接使用Window Media Player的COM组件及进行音频播放,也就是说你手机上WMP支持多少种格式,快速闹钟也将支持这些格式。如:.wma、.mp3、.mpeg、.avi、.wmv、.asf、.wav、.mid、.mid、.midi、.wm、.mpg、.m1v、 .mp2、.mpa、.rm、.3gp等目前绝大部分文件格式,当然有些是要求你安装了插件才行的,如现在流行rm插件。同时目前的PPC2003、WM5、WM6都可很好的支持以上文件。(PPC2003因WMP本身不支持WAV,所以此系统的用户们注意了。) 注:目前视频文件将只可以听到声音,以后将会在闹钟提醒界面支持视频。引用: 闹钟定时说明: 1。每周定时:   这个我想大家都知道的,选择你在一周内所有定时的日,每天选项意在提供快速选择一周内的七天。初始为每天。 2。每月定时:(支持农历)   在每月指定日将会启闹,如:设为5日,每月的5日其设定时间都会启闹,连续天数是在这5日起的N天内都有效。农历每月是一样的意思,同时每月定时支持两个定时日,意思一样,方便初一、十五类似的定时。 3。指定定时:(支持农历)   这个很好理解,不用多说。说下指定定时中的每年选项,这个选项适合定时生日或纪念日的朋友,先设置好起始年,再打上每年选项的勾,闹钟注释的内容中第一个"%d"的标志将被转换成当前年到您设置的起始年的差值,如:设为1982年,注释为"%d周岁生日",这样今年闹钟就会显示为:26周岁生日。以此类推。 4。周期定时:(支持农历)   是指从设定日起开启闹钟N天,然后关闭闹钟N天,之后重复,如果勾上每月的话,则每月在指定日都将重置,就是说循环周期将重新开始。农历是同样的意思,这个适合倒班的朋友。在农村赶圩,是逢一、六;二、七之类的,这样农历周期定时就用上场了,如逢一、六:设置为:农历、每月、指定日初一,开1天、关4天。引用: 重要声明:   有朋友反应会有不闹,或是出现异常问题。当您也出现这些问题时,请确认你是否将QuickAlarm放在了卡上。因存储卡在从休眠启动瞬间是无法正常使用的,所以在将QuickAlarm放在卡上,出现异常是在所能免的。其它闹钟软件多数解决方法都是将一个程序复制到Windows目录下,也就是QuickAlarm中的QAlarm文件。但现在QAlarm无法单独执行。请将QuickAlarm整个文件夹复制到您的手机内存里。以保证安全运行,不会出现不闹现象。(如果您觉得文件大,可将报时文件去掉。) 1.2   1:还原1.0版本高级选项,解决部分机型关机现象;   2:编辑框添加右键菜单的支持,提高方便性;   3:今日插件支持右键菜单操作,可关闭打开所有闹钟;   4:修复上版本一些小BUG;   5:标准化插件,提供定时程序插件源码,方便有能力者编写更好的插件。
module WashingMachine( input [6:0]time_param,//正反转时长 input clk, // 系统时钟 input stop, //停机按钮 input [1:0] mode, // 模式选择: 00-正常洗, 01-羊毛洗, 10-快洗 input ps, // 暂停开关 input lid_sw, // 上盖开关 (1:盖上, 0:打开) input water_in_sw, // 进水开关 (1:有进水, 0:无进水) output reg [6:0] seg, // 数码管(显示剩余秒数) output reg [6:0] zhuangtai, // 数码管(显示当前状态) output reg alarm // 报警信号 ); // 状态定义 parameter IDLE = 4'd0; // 待机状态 parameter PAUSE_INIT = 4'd1; // 初始待机 parameter FILL_WATER = 4'd2; // 进水 parameter WASH_FORWARD = 4'd3; // 正转洗涤 parameter PAUSE_MID = 4'd4; // 中间待机 parameter WASH_BACKWARD = 4'd5; // 反转洗涤 parameter FINISH = 4'd6; // 完成 parameter PAUSED = 4'd7; // 用户暂停 parameter FAULT = 4'd8; // 故障暂停 // 模式定义 parameter MODE_NORMAL = 2'b00; parameter MODE_WOOL = 2'b01; parameter MODE_QUICK = 2'b10; // 时钟分频 (50MHz -> 1Hz) reg [25:0] clk_div; wire clk_1s; assign clk_1s = (clk_div == 26'd49_999_999); always @(posedge clk or negedge stop) begin if (!stop) clk_div <= 0; else if (clk_div == 26'd49_999_999) clk_div <= 0; else clk_div <= clk_div + 1; end // 状态寄存器 reg [3:0] current_state, next_state; reg [3:0] saved_state; // 保存暂停前的状态 reg [7:0] timer; // 计时器 (0-255秒) reg [3:0] cycle_cnt; // 循环计数器 reg [7:0] time_param; // 当前步骤所需时间 reg [7:0] total_cycles; // 总循环次数 // 数码管显示 reg [15:0] display_num; // 显示的数值 reg [1:0] seg_count; // 数码管扫描计数器 reg [15:0] seg_clk_div; // 数码管扫描时钟分频 reg [3:0] digit; // 当前数码管显示的数字 // 状态转移 always @(posedge clk or negedge stop) begin if (!stop) current_state <= IDLE; else current_state <= next_state; end // 下一个状态逻辑 always @(*) begin next_state = current_state; // 停止按钮处理(最高优先级) if (stop && (current_state != IDLE) && (current_state != FINISH)) begin next_state = IDLE; end // 暂停按钮处理 else if ((ps && (current_state != IDLE) && (current_state != FINISH) && (current_state != PAUSED) && (current_state != FAULT)) begin next_state = PAUSED; end // 故障检测 else if ((current_state != IDLE) && (current_state != FINISH) && (current_state != PAUSED) &&(current_state != FAULT) && (!lid_sw || !water_in_sw)) begin next_state = FAULT; end else begin case (current_state) IDLE: begin if (start_btn && lid_sw && water_in_sw) next_state = PAUSE_INIT; end PAUSE_INIT: begin if (timer == 0) next_state = FILL_WATER; end FILL_WATER: begin if (timer == 0) next_state = WASH_FORWARD; end WASH_FORWARD: begin if (timer == 0) next_state = PAUSE_MID; end PAUSE_MID: begin if (timer == 0) next_state = WASH_BACKWARD; end WASH_BACKWARD: begin if (timer == 0) begin if (cycle_cnt < total_cycles) next_state = PAUSE_MID; else next_state = FINISH; end end PAUSED: begin if (start_btn && lid_sw && water_in_sw) next_state = saved_state; end FAULT: begin if (lid_sw && water_in_sw && start_btn) next_state = saved_state; end endcase end end // 计时器控制 always @(posedge clk or negedge stop) begin if (!stop) begin timer <= 0; cycle_cnt <= 0; saved_state <= IDLE; alarm <= 0; end else begin // 保存状态用于恢复 if ((next_state == PAUSED || next_state == FAULT) && (current_state != PAUSED) && (current_state != FAULT)) begin saved_state <= current_state; end // 状态进入时的初始化 case (next_state) IDLE: begin timer <= 0; cycle_cnt <= 0; alarm <= 0; end PAUSE_INIT: begin timer <= 8'd2; // 2秒待机 cycle_cnt <= 0; end FILL_WATER: begin timer <= 8'd3; // 3秒进水 end WASH_FORWARD: begin timer <= time_param; // 正转时间 end PAUSE_MID: begin timer <= 8'd1; // 1秒待机 end WASH_BACKWARD: begin timer <= time_param; // 反转时间 end FINISH: begin alarm <= 1; // 启动报警 end FAULT: begin alarm <= 1; // 故障报警 end endcase // 计时器递减 if (clk_1s && (current_state != IDLE) && (current_state != PAUSED) && (current_state != FAULT) && (current_state != FINISH) && timer > 0) begin timer <= timer - 1; end // 循环计数(在反转洗涤结束前1秒增加计数) if (clk_1s && (current_state == WASH_BACKWARD) && (timer == 1)) begin cycle_cnt <= cycle_cnt + 1; end end end // 模式参数设置 always @(*) begin case (mode) MODE_NORMAL: begin // 正常洗 time_param = 8'd10; // 正/反转时间 total_cycles = 8'd7; // 循环次数 end MODE_WOOL: begin // 羊毛洗 time_param = 8'd5; // 正/反转时间 total_cycles = 8'd10; // 循环次数 end MODE_QUICK: begin // 快洗 time_param = 8'd10; // 正/反转时间 total_cycles = 8'd5; // 循环次数 end default: begin // 默认正常洗 time_param = 8'd10; total_cycles = 8'd7; end endcase end // 数码管状态指示 always @(*) begin case (current_state) IDLE: zhuangtai = 7'b1000000; PAUSE_INIT: zhuangtai = 7'b1111001; FILL_WATER: zhuangtai = 7'b0100100; WASH_FORWARD: zhuangtai = 7'b0110000; WASH_BACKWARD: zhuangtai = 7'b0011001; PAUSE_MID: zhuangtai = 7'b0010010; PAUSED: zhuangtai = 7'b0000010; FAULT: zhuangtai = 7'b1111000; FINISH: zhuangtai = 7'b0000000; default: zhuangtai = 7'b1111111; endcase end // 数码管扫描(约1KHz刷新率) always @(posedge clk or negedge stop) begin if (!stop) begin seg_count <= 0; seg_clk_div <= 0; end else begin if (seg_clk_div >= 16'd50_000) begin seg_count <= seg_count + 1; seg_clk_div <= 0; end else begin seg_clk_div <= seg_clk_div + 1; end end end // 显示内容选择 always @(*) begin case (current_state) IDLE: display_num = 16'h0000; // 显示0 FINISH: display_num = 16'h8888; // 全显示8 PAUSED, FAULT: display_num = {8'd0, timer}; // 显示剩余时间 default: display_num = {8'd0, timer}; // 显示剩余时间 endcase case (seg_count) 2'b00: begin an = 4'b1110; // 最右边数码管 digit = display_num[3:0]; end 2'b01: begin an = 4'b1101; // 右数第二个数码管 digit = display_num[7:4]; end 2'b10: begin an = 4'b1011; // 右数第三个数码管 digit = display_num[11:8]; end 2'b11: begin an = 4'b0111; // 最左边数码管 digit = display_num[15:12]; end endcase end // 数码管译码(共阴极) always @(*) begin case (digit) 4'h0: seg = 7'b1000000; // 0 4'h1: seg = 7'b1111001; // 1 4'h2: seg = 7'b0100100; // 2 4'h3: seg = 7'b0110000; // 3 4'h4: seg = 7'b0011001; // 4 4'h5: seg = 7'b0010010; // 5 4'h6: seg = 7'b0000010; // 6 4'h7: seg = 7'b1111000; // 7 4'h8: seg = 7'b0000000; // 8 4'h9: seg = 7'b0010000; // 9 4'hA: seg = 7'b0001000; // A default: seg = 7'b1111111; // 全灭 endcase end endmodule
最新发布
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值