Handler 接口类
**
* 定义处理接口
*/
public interface Handler {
/**
* 有抽象类的子类进行实现
* @param posInfo
* @param ruleParam
* @param ctx
* @param vehicleState
* @param alarmState
* @param out
* @throws Exception
*/
void handle(VehiclePosInfo posInfo, RuleParam ruleParam, KeyedBroadcastProcessFunction.ReadOnlyContext ctx, ValueState<VehicleState> vehicleState, MapState<String, AlarmEvent> alarmState, Collector<AlarmEvent> out, Map<Long, LimitSpeedData> limitSpeedDataMap) throws Exception;
}
基础处理器抽象类 StreamHandler.java
/**
* 基础处理器抽象类
*/
@Slf4j
public abstract class StreamHandler implements Handler {
/**
* 处理器名称
*/
protected String handlerName;
protected StreamHandler(String handlerName) {
this.handlerName = handlerName;
}
public String getHandlerName() {
return handlerName;
}
/**
* 获取报警附加信息抽象方法,由子类去实现,非报警业务无需实现该方法
*
* @param extend
* @param englishName
* @param beginEvent 开始报警
* @return
*/
protected abstract String getAlarmAttachInfo(Map<String, Object> extend, RuleParam ruleParam, String englishName, boolean beginEvent);
/**
* 获取报警附加信息抽象方法,由子类去实现,非报警业务无需实现该方法
*
* @param extend
* @param englishName
* @param beginEvent 开始报警
* @return
*/
protected abstract String getPathName(Map<String, Object> extend, RuleParam ruleParam, String englishName, boolean beginEvent);
/**
* 模板方法,做一些统一处理工作,过滤补传数据,设置行停状态等
*
* @param posInfo
* @param ctx
* @param lastVehicleState
* @param alarmState
* @param out
*/
public void handleStream(VehiclePosInfo posInfo, RuleParam ruleParam, KeyedBroadcastProcessFunction.ReadOnlyContext ctx, ValueState<VehicleState> lastVehicleState, MapState<String, AlarmEvent> alarmState, Collector<AlarmEvent> out, Map<Long, LimitSpeedData> limitSpeedDataMap) throws Exception {
handle(posInfo, ruleParam, ctx, lastVehicleState, alarmState, out,limitSpeedDataMap);
}
/**
* 判断停车
*
* @return
*/
protected boolean isStop(VehiclePosInfo posInfo) {
return posInfo.getSpeed() == 0 || posInfo.getSpeed() < 5;
}
/**
* 判断运行
*
* @return
*/
protected boolean isRun(VehiclePosInfo posInfo) {
return posInfo.getSpeed() > 5;
}
/**
* @param englishAlarmName
* @param posInfo
* @param ruleParam
* @param ctx
* @param lastVehicleState
* @param alarmState
* @param out
*/
protected void endAlarm(String englishAlarmName, VehiclePosInfo posInfo, RuleParam ruleParam, KeyedBroadcastProcessFunction.ReadOnlyContext ctx, ValueState<VehicleState> lastVehicleState, MapState<String, AlarmEvent> alarmState, Collector<AlarmEvent> out) throws Exception {
this.alarmHandle(englishAlarmName, posInfo, ruleParam, ctx, lastVehicleState, alarmState, out, false, true);
}
/**
* @param englishAlarmName
* @param posInfo
* @param ruleParam
* @param ctx
* @param lastVehicleState
* @param alarmState
* @param out
*/
protected void beginAlarm(String englishAlarmName, VehiclePosInfo posInfo, RuleParam ruleParam, KeyedBroadcastProcessFunction.ReadOnlyContext ctx, ValueState<VehicleState> lastVehicleState, MapState<String, AlarmEvent> alarmState, Collector<AlarmEvent> out) throws Exception {
this.alarmHandle(englishAlarmName, posInfo, ruleParam, ctx, lastVehicleState, alarmState, out, true, false);
}
/**
* 通用报警处理方法
*
* @param englishAlarmName
* @param posInfo
* @param ruleParam
* @param ctx
* @param lastVehicleState
* @param alarmState
* @param out
* @param existAlarm
* @param lastExistAlarm
* @throws Exception
*/
protected void alarmHandle(String englishAlarmName, VehiclePosInfo posInfo, RuleParam ruleParam, KeyedBroadcastProcessFunction.ReadOnlyContext ctx, ValueState<VehicleState> lastVehicleState, MapState<String, AlarmEvent> alarmState, Collector<AlarmEvent> out, Boolean existAlarm, Boolean lastExistAlarm) throws Exception {
//情况1 当前报警
if (existAlarm) {
//1.1当前报警,上次不报警 ,产生开始报警事件(如果离线,也表示上次不报警)
if (!lastExistAlarm) {
// 获取是否高速上的标志
//1.1.1产生开始报警事件
AlarmEvent beginAlarmEvent = beginAlarmEvent(posInfo, lastVehicleState.value(), ruleParam, englishAlarmName, ctx);
//1.1.2中间状态记录该报警事件
alarmState.put(englishAlarmName, beginAlarmEvent);
//1.1.3发送报警
log.info("开始报警:{}", JSON.toJSONString(beginAlarmEvent));
out.collect(beginAlarmEvent);
//线上模式
if (!Main.localMode) {
RedisDataUtil.updateAlarm(beginAlarmEvent,ctx);
}
}
//1.2当前报警,上次报警 ,产生持续报警事件
if (lastExistAlarm) {
//1.2.1获取开始报警事件
AlarmEvent beginAlarmEvent = alarmState.get(englishAlarmName);
//持续报警次数加 1
beginAlarmEvent.setAlarmNum(beginAlarmEvent.getAlarmNum() + 1);
beginAlarmEvent.setSpeed(posInfo.getSpeed());
//更新中间数据状态
alarmState.put(englishAlarmName, beginAlarmEvent);
}
}
// 情况2 当前不报警
if (!existAlarm) {
//2.1当前不报警,上次报警,产生结束报警事件,清除报警
if (lastExistAlarm) {
//2.1.1获取开始报警事件
AlarmEvent beginAlarmEvent = alarmState.get(englishAlarmName);
// //2.1.2 产生结束报警事件
AlarmEvent endAlarmEvent = endAlarmEvent(posInfo, lastVehicleState.value().getLastPosInfo(), englishAlarmName, ruleParam, beginAlarmEvent);
//2.1.3 发送结束报警事件
if (endAlarmEvent !=null){
out.collect(endAlarmEvent);
//2.1.4 中间状态清除该报警事件
log.info("删除报警车牌:{}",posInfo.getPlate());
alarmState.remove(englishAlarmName);
//线上模式
if (!Main.localMode) {
RedisDataUtil.updateAlarm(endAlarmEvent,ctx);
log.info("结束报警:{}", JSON.toJSONString(endAlarmEvent));
}
}
}
//2.2当前不报警,上次不报警 (不做任何处理)
if (!lastExistAlarm) {
}
}
}
/**
* 构建开始报警事件
*
* @param posInfo
* @param englishAlarmName
* @return
*/
protected AlarmEvent beginAlarmEvent(VehiclePosInfo posInfo, VehicleState state, RuleParam ruleParam, String englishAlarmName, KeyedBroadcastProcessFunction.ReadOnlyContext ctx) {
AlarmDefine alarmDefine = AlarmTypeCache.getAlarmDefine(englishAlarmName);
if(alarmDefine==null){
log.info("alarmDefine信息为空:{}",englishAlarmName);
return null;
}
AlarmEvent event = new AlarmEvent();
//设置基础属性
setBaseProp(posInfo, event,alarmDefine);
//结束status为1
event.setStatus((byte) 1);
event.setAlarmId(IdGenUtil.nextId());
event.setAlarmNum(1);
event.setAlarmClass(alarmDefine.getAlarmClass());
event.setAlarmType(alarmDefine.getAlarmType());
event.setChineseName(alarmDefine.getChineseName());
event.setEnglishName(alarmDefine.getEnglishName());
return event;
}
**
* 设置基础属性
*
* @param posInfo
* @param event
*/
protected void setBaseProp(VehiclePosInfo posInfo, AlarmEvent event,AlarmDefine alarmDefine) {
event.setVehicleId(posInfo.getVehicleId());
event.setGroupId(posInfo.getGroupId());
event.setPlate(posInfo.getPlate());
event.setPlateColorByte(posInfo.getPlateColor());
event.setEventTime(posInfo.getDevTime() != null ? posInfo.getDevTime().getTime() : 0);
event.setCompanyGroupId(GroupCache.getCompanyId(Long.valueOf(event.getGroupId())));
event.setLat(posInfo.getLat());
event.setDirect(posInfo.getDirect() != null ? new Double(posInfo.getDirect()).intValue() : 0);
event.setLon(posInfo.getLon());
event.setType((byte) 2);
event.setSpeed(posInfo.getSpeed());
event.setVehicleShape(posInfo.getVehicleShape());
}
/**
* 构建结束报警事件
*
* @param posInfo
* @param englishAlarmName
* @param beginAlarmEvent
* @return
*/
protected AlarmEvent endAlarmEvent(VehiclePosInfo posInfo, VehiclePosInfo last, String englishAlarmName, RuleParam ruleParam, AlarmEvent beginAlarmEvent) {
.....具体逻辑
}
/**
* 判断设备时间是否在时间范围内 精确到分钟 例:22:00_05:00、22:00:00_05:00:00 21:00_23:00
*
* @param rule
* @param date
* @return
*/
protected static boolean isInTimeFrame(String rule, Date date){
LocalDateTime currTime = LocalDateTimeUtil.of(date.getTime(), ZoneId.systemDefault());
String currTimeStr = LocalDateTimeUtil.format(currTime, "HH:mm:ss");
String[] times = null;
if (rule.contains("_")){
times = rule.split("_");
}else if (rule.contains("-")){
times = rule.split("-");
}
String startTime = null;
String endTime = null;
if (times[0].split(":").length==2){
startTime = times[0]+":00";
endTime = times[1]+":00";
} else {
startTime = times[0];
endTime = times[1];
}
if(startTime.compareTo(endTime)>0){
if(currTimeStr.compareTo(startTime)>=0||currTimeStr.compareTo(endTime)<=0){
return true;
}
}else if(startTime.compareTo(endTime)<0){
if(currTimeStr.compareTo(startTime)>=0&&currTimeStr.compareTo(endTime)<=0){
return true;
}
}
return false;
}
/**
* 判断两次定位数据是否夸天
*
* @return
*/
protected boolean isCrossDay(Date currentDevTime, Date lastDevTime) {
if (currentDevTime != null && lastDevTime != null) {
return currentDevTime.getDay() != lastDevTime.getDay();
}
return false;
}
/**
* @Title: timeDifference
* @Description: 两个时间相差多少天多少小时多少分
* @param @param t1
* @param @param t2
* @param @return 参数
* @return String 返回类型
* @throws
*/
public static String timeDifference(long diff) {
StringBuffer buf = new StringBuffer();
// 时间差
// long diff = Math.abs(t1 - t2);
// 一天的毫秒数
long nd = 1000 * 24 * 60 * 60;
// 一小时的毫秒数
long nh = 1000 * 60 * 60;
// 一分钟的毫秒数
long nm = 1000 * 60;
// 一秒钟的毫秒数
long ns = 1000;
// 多少天
long day = diff / nd;
// 多少小时
long hour = diff % nd / nh;
// 多少分
long min = diff % nd % nh / nm;
// 多少秒
long sec = diff % nd % nh % nm / ns;
// x天 或者 ""
buf.append(day > 0 ? day + "天" : "");
// x时 或者 x时x分
buf.append(hour > 0 ? hour + "小时" : (min > 0 ? (day > 0 ? hour + "小时" : "") + min + "分钟" : ""));
// x分 或者 ""
buf.append((hour > 0 && min > 0) ? min + "分钟" : "");
// x秒 或者 ""
buf.append((sec > 0) ? sec + "秒" : "");
// 结果
return buf.toString();
}
}
具体业务的例子:超速报警分析
OverSpeedPlatform.java
**
* @author ljy
* 平台超速业务处理类
*/
@Slf4j
public class OverSpeedPlatform extends StreamHandler {
public final static String OVER_SPEED = "overSpeedPlatform";
public static String vehicleShape = null;
public OverSpeedPlatform() {
super("平台超速报警处理类");
}
@Override
public void handle(VehiclePosInfo posInfo, RuleParam ruleParam, KeyedBroadcastProcessFunction.ReadOnlyContext ctx, ValueState<VehicleState> vehicleState, MapState<String, AlarmEvent> alarmState, Collector<AlarmEvent> out, Map<Long, LimitSpeedData> limitSpeedDataMap) throws Exception {
VehicleState state = vehicleState.value();
VehiclePosInfo last = state.getLastPosInfo();
boolean lastExistOverSpeedAlarm = alarmState.contains(OVER_SPEED);
//离线需要结束掉报警
if (isOffline(posInfo, last) && lastExistOverSpeedAlarm) {
this.endAlarm(OVER_SPEED, posInfo, ruleParam, ctx, vehicleState, alarmState, out);
lastExistOverSpeedAlarm = false;
}
vehicleShape = posInfo.getVehicleShape();
if (vehicleShape == null) {
return;
}
// 获取平台超速报警规则
GeneralRule rulePlatform = ruleParam.getGeneralRuleMap().get(GeneralRuleType.OVER_SPEED_PLATFORM);
if (posInfo.getPlate().equals(plate)){
log.info("规则{}", JSON.toJSONString(rulePlatform));
}
// 规则参数
String params;
// 规则数组
String [] param;
// 车辆类型
String vehicleShapeRule = null;
// 限速值
String speedlimit = null;
// 持续时长
String duration = null;
// 超速比
String overSpeedRatio = null;
if (rulePlatform !=null){
params = rulePlatform.getParam();
if (params!=null){
param = params.split(",");
if (param.length == 4){
vehicleShapeRule = param[0];
speedlimit = param[1];
duration = param[2];
overSpeedRatio = param[3];
}
}
}
// 当规则车型为空时,判断上次是否存在报警,存在结束掉报警
if (StringUtils.isBlank(vehicleShapeRule)) {
if(lastExistOverSpeedAlarm){
this.endAlarm(OVER_SPEED, posInfo, ruleParam, ctx, vehicleState, alarmState, out);
}
return;
}
if (StringUtils.isNotBlank(vehicleShapeRule) && !vehicleShape.equals(vehicleShapeRule)){
if (posInfo.getPlate().equals(plate)) {
log.info("当前车辆类型跟规则类型不一致");
}
if(lastExistOverSpeedAlarm){
this.endAlarm(OVER_SPEED, posInfo, ruleParam, ctx, vehicleState, alarmState, out);
}
return;
}else {
if(lastExistOverSpeedAlarm){
this.endAlarm(OVER_SPEED, posInfo, ruleParam, ctx, vehicleState, alarmState, out);
}
return;
}
boolean existOverSpeedAlarm = existOverSpeedAlarm(posInfo, state, speedlimit, duration, overSpeedRatio);
if(existOverSpeedAlarm){
// 根据超速比推送报警等级
// Integer alarmPushLevel = alarmPushAccordingToSpeedProportion(lastExistOverSpeedAlarm,existOverSpeedAlarm,ruleParam,OVER_SPEED,posInfo,out,alarmState,vehicleState,speedlimit);
posInfo.getExtend().put(posInfo.getVehicleShape()+"超速比",speedlimit);
// posInfo.getExtend().put("alarmPushLevel",alarmPushLevel);
this.alarmHandle(OVER_SPEED, posInfo, ruleParam, ctx, vehicleState, alarmState, out, true, lastExistOverSpeedAlarm);
// 这里做风险逻辑处理
// DealRiskPush.dealRiskLevelData(vehicleState, alarmState, out, OVER_SPEED, lastExistOverSpeedAlarm, true, posInfo,Double.parseDouble(speedlimit),false);
} else if (lastExistOverSpeedAlarm) {
// 这里做风险逻辑处理
// DealRiskPush.dealRiskLevelData(vehicleState, alarmState, out, OVER_SPEED, true, false, posInfo,Double.parseDouble(speedlimit),false);
this.alarmHandle(OVER_SPEED, posInfo, ruleParam, ctx, vehicleState, alarmState, out, false, true);
//不存在报警,清空报警开始时间
state.setStartOverSpeedTime(null);
}
}
@Override
protected String getPathName(Map<String, Object> extend, RuleParam ruleParam, String englishName, boolean beginEvent) {
return null;
}
private boolean existOverSpeedAlarm(VehiclePosInfo posInfo, VehicleState state, String speedLimit, String duration,String overSpeedRate) {
boolean existAlarm = false;
if (StringUtils.isNotBlank(speedLimit)) {
boolean sign = posInfo.getSpeed().intValue() > (Double.parseDouble(speedLimit) + (Double.parseDouble(speedLimit) * (overSpeedRate==null?0:Double.parseDouble(overSpeedRate) / 100)));
if (sign) {
//判断是否是第一次
if (state.getStartOverSpeedTime() == null) {
state.setStartOverSpeedTime(posInfo.getDevTime());
} else {
//判断是否大于规则设置的持续时间
if (posInfo.getDevTime().getTime() - state.getStartOverSpeedTime().getTime() > (duration == null?0:Integer.parseInt(duration))*1000) {
existAlarm = true;
}
}
}
}
return existAlarm;
}
@Override
protected String getAlarmAttachInfo(Map<String, Object> extend, RuleParam ruleParam, String englishName, boolean beginEvent) {
if(OVER_SPEED.equals(englishName)){
if (StringUtils.isNotBlank(vehicleShape)) {
if(extend.get(vehicleShape+"超速比")!=null){
return (String) extend.get(vehicleShape+"超速比");
}
}
}
return null;
}
}