flatmap
package pers.aishuang.flink.streaming.function.flatmap;
import org.apache.flink.streaming.api.functions.co.RichCoFlatMapFunction;
import org.apache.flink.util.Collector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pers.aishuang.flink.streaming.entity.OnlineDataObj;
import pers.aishuang.flink.streaming.entity.VehicleInfoModel;
import pers.aishuang.flink.streaming.utils.DateUtil;
import java.util.HashMap;
public class VehicleInfoMapMysqlFunction extends RichCoFlatMapFunction<
OnlineDataObj, HashMap<String,VehicleInfoModel>, OnlineDataObj> {
//创建日志打印器
private static final Logger logger = LoggerFactory.getLogger(VehicleInfoMapMysqlFunction.class);
//定义广播变量
private static HashMap<String, VehicleInfoModel> vehicleInfoModelConfig = null;
@Override
public void flatMap1(OnlineDataObj value, Collector<OnlineDataObj> out) throws Exception {
//1. 通过vin 获取到车辆基础信息
String vin = value.getVin();
VehicleInfoModel vehicleInfoModel = vehicleInfoModelConfig.getOrDefault(vin, null);
//2. 如果车辆基础信息不为空
if(vehicleInfoModel != null) {
//--将车系seriesName、车型modelName、车限liveTime、销售日期saleDate,车辆类型carType封装到onlineDataObj对象中
value.setModelName(vehicleInfoModel.getModelName());
value.setSeriesName(vehicleInfoModel.getSeriesName());
value.setLiveTime(
(System.currentTimeMillis() - DateUtil.convertStringToDateTime(vehicleInfoModel.getSalesDate())
.getTime()) / 1000 / 3600 / 24 / 30 + ""
);
value.setSalesDate(vehicleInfoModel.getSalesDate());
value.setCarType(vehicleInfoModel.getCarType());
//-- 将onlineDataObj收集返回
out.collect(value);
//-- 打印输出,基础信息不存在
}else {
logger.error("当前上报车辆,未在库里记录");
}
}
@Override
public void flatMap2(HashMap<String, VehicleInfoModel> value, Collector<OnlineDataObj> out) throws Exception {
vehicleInfoModelConfig = value;
}
}
map
package pers.aishuang.flink.streaming.function.map;
import com.alibaba.fastjson.JSON;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.hadoop.hbase.util.Bytes;
import pers.aishuang.flink.streaming.entity.ItcastDataPartObj;
import pers.aishuang.flink.streaming.utils.GeoHashUtil;
import pers.aishuang.flink.streaming.utils.RedisUtil;
import pers.aishuang.flink.streaming.entity.VehicleLocationModel;
/**
* 经纬度坐标只能表示一块区域,实际生活中不存在点,因为点的大小可以无穷小
* geoHash算法原理:
* 将经度和纬度值转化为2进制数字,以纬度在奇数位,经度在偶数位,将二者穿插合并成一个2进制数字
* 再基于base32编码将其转成字符串,字符串越长,位置越精准
*/
public class LocactionInfoReidsFunction extends RichMapFunction<ItcastDataPartObj,ItcastDataPartObj> {
@Override
public ItcastDataPartObj map(ItcastDataPartObj value) throws Exception {
//1. 获取车辆数据的经度和纬度生成 geohash(经度,纬度)-> geohash字符串 -> 地理详细位置
Double lng = value.getLng();
Double lat = value.getLat();
String geohash = GeoHashUtil.encode(lat, lng);
//2. 根据geohash 从redis中获取value值(geohash在redis中是作为主键存在)
byte[] locationDetailArr = RedisUtil.get(Bytes.toBytes(geohash));
//3. 如果查询出来的值不为空,将其通过JSON对象转换成VehicleLocationModel对象,否则置为null
if(locationDetailArr != null && locationDetailArr.length > 0) {
String vehicleLocationJson = Bytes.toString(locationDetailArr);
VehicleLocationModel model = JSON.parseObject(vehicleLocationJson,VehicleLocationModel.class);
//4. 如果当前对象不为空,将国家,省市区地址赋给itcastDataPartObj,否则置为null
if(model != null) {
value.setCountry(model.getCountry());
value.setProvince(model.getProvince());
value.setCity(model.getCity());
value.setDistrict(model.getDistrict());
value.setAddress(model.getAddress());
} else {
value.setCountry(null);
value.setProvince(null);
value.setCity(null);
value.setDistrict(null);
value.setAddress(null);
}
}else {
value.setCountry(null);
value.setProvince(null);
value.setCity(null);
value.setDistrict(null);
value.setAddress(null);
}
//5. 返回数据
return value;
}
}
window
package pers.aishuang.flink.streaming.function.window;
import com.clearspring.analytics.util.Lists;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.flink.api.common.state.MapState;
import org.apache.flink.api.common.state.MapStateDescriptor;
import org.apache.flink.api.common.state.StateTtlConfig;
import org.apache.flink.api.common.time.Time;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.windowing.RichWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pers.aishuang.flink.streaming.entity.ElectricFenceModel;
import java.util.Collections;
import java.util.List;
/**
* 考虑问题:时间滚动窗口,有的窗口没有数据,flink是怎么处理的
*/
public class ElectricFenceWindowFunction extends RichWindowFunction<ElectricFenceModel, ElectricFenceModel,
String , TimeWindow> {
//创建logger
private static final Logger logger = LoggerFactory.getLogger(ElectricFenceWindowFunction.class);
//1.定义存储历史电子围栏数据的state,<vin,是否在电子围栏内0:内, 1:外> MapState<String, Integer>
MapState<String, Byte> lastState = null;
@Override
public void open(Configuration parameters) throws Exception {
//1. 定义mapState的描述器,就是定义state的结构
MapStateDescriptor<String, Byte> lastStateDesc = new MapStateDescriptor<String, Byte>("lastState",String.class,Byte.class);
//2. 获取parameterTool,用来读取配置文件参数
StateTtlConfig stateTtlConfig = StateTtlConfig
//设置状态有效时间
.newBuilder(Time.seconds(180))
//设置状态更新类型:更新
.setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
//设置已过期但还未被清理的状态如何处理
.setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
//过期对象的清理策略
.cleanupFullSnapshot()
.build();
//3. 设置state的ttl
lastStateDesc.enableTimeToLive(stateTtlConfig);
//4. 获取当前运行环境 所描述的那个MapState
lastState = getRuntimeContext().getMapState(lastStateDesc);
}
@Override
public void close() throws Exception {
super.close();
}
@Override
public void apply(String key, TimeWindow window, Iterable<ElectricFenceModel> input, Collector<ElectricFenceModel> out) throws Exception {
//1. 创建返回对象
ElectricFenceModel electricFenceModel = new ElectricFenceModel();
//2. 对窗口内的数据进行排序(按“终端时间戳”属性进行升序排序)
List<ElectricFenceModel> electricFenceModelList = Lists.newArrayList(input);
Collections.sort(electricFenceModelList);
Collections.reverse(electricFenceModelList);
//3. 从state中获取车辆vin对应的上一次窗口电子围栏lastStateValue标记(车辆上一次窗口是否在电子围栏中)0:电子围栏内,1:电子围栏外
Byte lastStateValue = lastState.get(key);
//4. 如果上次状态为空,初始化赋值
if(lastStateValue == null ){
lastStateValue = -1;
}
//5. 判断当前处于电子围栏内还是电子围栏外
//-- 定义当前车辆电子围栏圈内出现的次数
long inElectricFence = electricFenceModelList.stream()
.filter(model -> model.getNowStatus() == 0)
.count();
//-- 定义当前车辆电子围栏圈外出现的次数
long outElectricFence = electricFenceModelList.stream()
.filter(model -> model.getNowStatus() == 1)
.count();
//6. 定义当前窗口的电子围栏状态,默认让当前状态为1,在电子围栏外
byte currentState = 1;
//7. 90s内车辆出现在电子围栏内的次数多于出现在电子围栏外的次数,则认为当前处于电子围栏内
if(inElectricFence >= outElectricFence){
currentState = 0;
}
//8. 将当前窗口的电子围栏状态写入到state中,供下次判断
lastState.put(key, currentState);
//9. 如果当前电子围栏状态与上一次电子围栏状态不同
//-- 如果上一次窗口处于电子围栏外,而本次是电子围栏内,则将进入电子围栏的时间写入到数据库中
if(lastStateValue == 1 && currentState == 0) {
//-- 进入栅栏,找到最后一条在外面的数据
//-- 出去栅栏,找到最后一条在里面的数据
ElectricFenceModel lastOutEleModel = electricFenceModelList
.stream()
.filter(model -> model.getNowStatus() == 1)
.findFirst()
.get();
//-- 拷贝属性给 electricFenceModel 并将进围栏终端时间赋值,并且将状态告警字段赋值为1 0:出围栏 1:进围栏,将数据collect返回
BeanUtils.copyProperties(electricFenceModel, lastOutEleModel);
electricFenceModel.setInEleTime(lastOutEleModel.getGpsTime());
electricFenceModel.setStatusAlarm(1);
out.collect(electricFenceModel);
} else if(lastStateValue == 0 && currentState == 1) {
//如果上一次窗口处于电子围栏内,而本次是电子围栏外
//-- 进入栅栏,找到最后一条在外面的数据
//-- 出去栅栏,找到最后一条在里面的数据
ElectricFenceModel lastInEleModel = electricFenceModelList
.stream()
.filter(model -> model.getNowStatus() == 0)
.findFirst()
.get();
//-- 拷贝属性给electricFenceModel 并将出栅栏终端时间赋值,并且将状态告警 0:出围栏,1:进围栏,将数据collect返回
BeanUtils.copyProperties(electricFenceModel,lastInEleModel);
electricFenceModel.setOutEleTime(lastInEleModel.getGpsTime());
electricFenceModel.setStatusAlarm(0);
out.collect(electricFenceModel);
}
}
}
package pers.aishuang.flink.streaming.function.window;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.flink.shaded.guava18.com.google.common.collect.Lists;
import org.apache.flink.streaming.api.functions.windowing.RichWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;
import pers.aishuang.flink.streaming.entity.ItcastDataPartObj;
import pers.aishuang.flink.streaming.entity.OnlineDataObj;
import pers.aishuang.flink.streaming.utils.DateUtil;
import java.util.ArrayList;
import java.util.Collections;
/**
* 对30s窗口内的数据生成一个新的对象,包含拉宽数据地理信息,实时上报数据
* 数据库中的静态数据 OnlineDataObj
* 开发步骤:
* 1、对数据进行排序
* 2、获取每一条数据
* 3、对每条数据判断是否有告警信息,alarm=1 说明是告警信息
* 4、封装成对象OnlineDataObj
* 5、返回
*/
//http请求超时时间和 redis会话超时都要小于水印乱序时间
public class OnlineStatisticsWindowFunction extends RichWindowFunction<
ItcastDataPartObj, OnlineDataObj,
String, TimeWindow> {
@Override
public void apply(String key, TimeWindow window, Iterable<ItcastDataPartObj> input, Collector<OnlineDataObj> out) throws Exception {
OnlineDataObj onlineDataObj = null;
//1. 对当前的数据集合进行升序排序
ArrayList<ItcastDataPartObj> itcastDataPartObjList = Lists.newArrayList(input);//guava的Lists工具类
Collections.sort(itcastDataPartObjList);
//第二种写法:
//Collections.sort(itcastDataPartObjList, Comparator.comparingLong(ItcastDataPartObj::getTerminalTimeStamp));
//要想降序的话,直接再反转就行:Collections.reverse(itcastDataPartObjList);
//2. 获取集合中第一条数据
ItcastDataPartObj firstItcastData = itcastDataPartObjList.get(0);
//3. 循环遍历每条数据,将集合中存在异常的数据拼接到指定属性中
for(ItcastDataPartObj itcastDataPartObj : itcastDataPartObjList) {
//30s窗口最多6条数据,每条数据需要检测19个字段,如果出现异常字段就进行 字符串拼接
//-- 过滤没有各种警告的信息,调用setOnlineDataObj 将每一条对象和每条对象和标识0 返回到OnlineDataObj,并收集这个对象
if(filterNoAlarm(itcastDataPartObj)){ //true:没有告警:0
onlineDataObj = setOnlineDataObj(firstItcastData, itcastDataPartObj, 0);
}
//否则 调用setOnlineDataObj 将第一条对象和每条对象和标识1 返回到OnlineDataObj,并收集这个对象
else {
onlineDataObj = setOnlineDataObj(firstItcastData,itcastDataPartObj,1);
}
out.collect(onlineDataObj);
}
}
/**
*
* @param firstItcastData
* @param itcastDataPartObj
* @param alarmFlag
* @return
*/
private OnlineDataObj setOnlineDataObj(ItcastDataPartObj firstItcastData, ItcastDataPartObj itcastDataPartObj, int alarmFlag) {
//1. 定义OnlineDataObj
OnlineDataObj onlineDataObj = new OnlineDataObj();
try {
//2. 将每条的对象属性拷贝到定义OnlineDataObj
BeanUtils.copyProperties(onlineDataObj,itcastDataPartObj);
//3. 将每条对象中表显历程赋值给mileage(已经有了,其实不用写)
onlineDataObj.setMileage(itcastDataPartObj.getTotalOdometer());
//4. 将告警信号赋值给isAlarm
onlineDataObj.setIsAlarm(alarmFlag);
//5. 将每个对象通过addAlarmNameList生成告警list,拼接成字符串赋值给alarmName,通过字符串join
onlineDataObj.setAlarmName(String.join("~",addAlarmNameList(itcastDataPartObj)));
//6. 将窗口内第一条数据告警时间赋值给earliestTime
onlineDataObj.setEarliestTime(firstItcastData.getTerminalTime());
//7. 将获取每条记录的充电状态通过getChargeState返回充电标识赋值给充电标记
onlineDataObj.setChargeFlag(getChargeState(itcastDataPartObj.getChargeStatus()));
//8. 将当前时间赋值给处理时间
onlineDataObj.setProcessTime(DateUtil.getCurrentDateTime());
} catch (Exception e) {
e.printStackTrace();
}
return onlineDataObj;
}
/**
* 根据充电状态返回充电标记
* @param chargeState
* @return
*/
private Integer getChargeState(Integer chargeState) {
int chargeFlag = -999999; //充电状态的初始值
//充电状态:0x01:停车充电、0x02:行车充电
if(chargeState == 1 || chargeState == 2 ){
chargeFlag = 1;
}
//0x04:充电完成 0x03:未充电
else if(chargeState == 4 || chargeState ==3){
chargeFlag = 0;
} else{
chargeFlag = 2;
}
return chargeFlag;
}
/**
* 将每条数据的故障名称追加到故障名称列表中
* @param itcastDataPartObj
* @return
*/
private ArrayList<String> addAlarmNameList(ItcastDataPartObj itcastDataPartObj) {
//定义故障名称列表对象
ArrayList<String> alarmNameList = new ArrayList<>();
//电池高温报警
if(itcastDataPartObj.getBatteryAlarm() == 1) {
alarmNameList.add("电池高温报警");
}
//单体电池高压报警
if(itcastDataPartObj.getSingleBatteryOverVoltageAlarm() == 1) {
alarmNameList.add("单体电池高压报警");
}
//电池单体一致性差报警
if(itcastDataPartObj.getBatteryConsistencyDifferenceAlarm() == 1) {
alarmNameList.add("电池单体一致性差报警");
}
//绝缘报警
if(itcastDataPartObj.getInsulationAlarm() == 1) {
alarmNameList.add("绝缘报警");
}
//高压互锁状态报警
if(itcastDataPartObj.getHighVoltageInterlockStateAlarm() == 1) {
alarmNameList.add("高压互锁状态报警");
}
//SOC跳变报警
if(itcastDataPartObj.getSocJumpAlarm() == 1) {
alarmNameList.add("SOC跳变报警");
}
//驱动电机控制器温度报警
if(itcastDataPartObj.getDriveMotorControllerTemperatureAlarm() == 1) {
alarmNameList.add("驱动电机控制器温度报警");
}
//DC-DC温度报警(dc-dc可以理解为车辆动力智能系统转换器)
if(itcastDataPartObj.getDcdcTemperatureAlarm() == 1) {
alarmNameList.add("DC-DC温度报警");
}
//SOC过高报警
if(itcastDataPartObj.getSocHighAlarm() == 1) {
alarmNameList.add("SOC过高报警");
}
//SOC低报警
if(itcastDataPartObj.getSocLowAlarm() == 1) {
alarmNameList.add("SOC低报警");
}
//温度差异报警
if(itcastDataPartObj.getTemperatureDifferenceAlarm() == 1) {
alarmNameList.add("温度差异报警");
}
//车载储能装置欠压报警
if(itcastDataPartObj.getVehicleStorageDeviceUndervoltageAlarm() == 1) {
alarmNameList.add("车载储能装置欠压报警");
}
//DC-DC状态报警
if(itcastDataPartObj.getDcdcStatusAlarm() == 1) {
alarmNameList.add("DC-DC状态报警");
}
//单体电池欠压报警
if(itcastDataPartObj.getSingleBatteryUnderVoltageAlarm() == 1) {
alarmNameList.add("单体电池欠压报警");
}
//可充电储能系统不匹配报警
if(itcastDataPartObj.getRechargeableStorageDeviceMismatchAlarm() == 1) {
alarmNameList.add("可充电储能系统不匹配报警");
}
//车载储能装置过压报警
if(itcastDataPartObj.getVehicleStorageDeviceOvervoltageAlarm() == 1) {
alarmNameList.add("车载储能装置过压报警");
}
//制动系统报警
if(itcastDataPartObj.getBrakeSystemAlarm() == 1) {
alarmNameList.add("制动系统报警");
}
//驱动电机温度报警
if(itcastDataPartObj.getDriveMotorTemperatureAlarm() == 1) {
alarmNameList.add("驱动电机温度报警");
}
//车载储能装置类型过充报警
if(itcastDataPartObj.getVehiclePureDeviceTypeOvercharge() == 1) {
alarmNameList.add("车载储能装置类型过充报警");
}
return alarmNameList;
}
/**
* 判断是否存在报警的字段
* @param itcastDataPartObj
* @return
*/
private boolean filterNoAlarm(ItcastDataPartObj itcastDataPartObj) {
//正常:0 , 异常:1
if(
//电池高温报警
itcastDataPartObj.getBatteryAlarm() == 1 ||
//单体电池高压报警
itcastDataPartObj.getSingleBatteryOverVoltageAlarm() == 1 ||
//电池单体一致性差报警
itcastDataPartObj.getBatteryConsistencyDifferenceAlarm() == 1 ||
//绝缘报警
itcastDataPartObj.getInsulationAlarm() == 1 ||
//高压互锁状态报警
itcastDataPartObj.getHighVoltageInterlockStateAlarm() == 1 ||
//SOC跳变报警
itcastDataPartObj.getSocJumpAlarm() == 1 ||
//驱动电机控制器温度报警
itcastDataPartObj.getDriveMotorControllerTemperatureAlarm() == 1 ||
//DC-DC温度报警(dc-dc可以理解为车辆动力智能系统转换器)
itcastDataPartObj.getDcdcTemperatureAlarm() ==1 ||
//SOC过高报警
itcastDataPartObj.getSocHighAlarm() == 1||
//SOC低报警
itcastDataPartObj.getSocLowAlarm() == 1 ||
//温度差异报警
itcastDataPartObj.getTemperatureDifferenceAlarm() == 1||
//车载储能装置欠压报警
itcastDataPartObj.getVehicleStorageDeviceUndervoltageAlarm() == 1||
//DC-DC状态报警
itcastDataPartObj.getDcdcStatusAlarm() == 1||
//单体电池欠压报警
itcastDataPartObj.getSingleBatteryUnderVoltageAlarm() == 1||
//可充电储能系统不匹配报警
itcastDataPartObj.getRechargeableStorageDeviceMismatchAlarm() == 1||
//车载储能装置过压报警
itcastDataPartObj.getVehicleStorageDeviceOvervoltageAlarm() == 1||
//制动系统报警
itcastDataPartObj.getBrakeSystemAlarm() == 1 ||
//驱动电机温度报警
itcastDataPartObj.getDriveMotorTemperatureAlarm() == 1 ||
//车载储能装置类型过充报警
itcastDataPartObj.getVehiclePureDeviceTypeOvercharge() == 1
){
return false;
}else {
return true;
}
}
}