Commit 1f8737a4 by 吴延飞

旅行信息指标计算,发布版。已自测通过

parent 7ae795b6
package com.zhht.irn.consts;
/**
* 常量类
*
* @author 吴延飞
* @date 2022-11-16 13:41:00
*/
public class Constant {
public static final String CYCLE_TOPIC_NAME = "signal_cycle_data";
public static final String TRAVEL_TOPIC_NAME = "trips_info";
public static final String TRAVEL_EVENT_TOPIC_NAME = "trips_event_info";
// 周期计算的延迟时间 15秒
public static final int CYCLE_CALCULATE_LATENESS = 15 * 1000;
}
package com.zhht.irn.entity; package com.zhht.irn.entity;
import java.util.Date;
public class Location { public class Location {
// 旅行记录ID
private String recordId;
// 位置的经度 // 位置的经度
private Double longitude; private Double longitude;
...@@ -13,7 +14,7 @@ public class Location { ...@@ -13,7 +14,7 @@ public class Location {
private Double speed; private Double speed;
// 此时的时间 // 此时的时间
private Date dtTranjectory; private String dtTranjectory;
// 此时车辆所处的车道ID // 此时车辆所处的车道ID
private String lineId; private String lineId;
...@@ -42,11 +43,11 @@ public class Location { ...@@ -42,11 +43,11 @@ public class Location {
this.speed = speed; this.speed = speed;
} }
public Date getDtTranjectory() { public String getDtTranjectory() {
return dtTranjectory; return dtTranjectory;
} }
public void setDtTranjectory(Date dtTranjectory) { public void setDtTranjectory(String dtTranjectory) {
this.dtTranjectory = dtTranjectory; this.dtTranjectory = dtTranjectory;
} }
...@@ -57,4 +58,24 @@ public class Location { ...@@ -57,4 +58,24 @@ public class Location {
public void setLineId(String lineId) { public void setLineId(String lineId) {
this.lineId = lineId; this.lineId = lineId;
} }
public String getRecordId() {
return recordId;
}
public void setRecordId(String recordId) {
this.recordId = recordId;
}
@Override
public String toString() {
return "Location{" +
"recordId='" + recordId + '\'' +
", longitude=" + longitude +
", latitude=" + latitude +
", speed=" + speed +
", dtTranjectory='" + dtTranjectory + '\'' +
", lineId='" + lineId + '\'' +
'}';
}
} }
...@@ -26,6 +26,12 @@ public class Travel { ...@@ -26,6 +26,12 @@ public class Travel {
// 记录时间 // 记录时间
private String recordTime; private String recordTime;
// 旅行开始时间
private String travelBeginTime;
// 旅行结束时间
private String travelEndTime;
// 车辆ID // 车辆ID
private Long carId; private Long carId;
...@@ -151,6 +157,22 @@ public class Travel { ...@@ -151,6 +157,22 @@ public class Travel {
this.recordTime = recordTime; this.recordTime = recordTime;
} }
public String getTravelBeginTime() {
return travelBeginTime;
}
public void setTravelBeginTime(String travelBeginTime) {
this.travelBeginTime = travelBeginTime;
}
public String getTravelEndTime() {
return travelEndTime;
}
public void setTravelEndTime(String travelEndTime) {
this.travelEndTime = travelEndTime;
}
public Long getCarId() { public Long getCarId() {
return carId; return carId;
} }
...@@ -399,7 +421,6 @@ public class Travel { ...@@ -399,7 +421,6 @@ public class Travel {
this.maxSpeed = maxSpeed; this.maxSpeed = maxSpeed;
} }
@Override @Override
public String toString() { public String toString() {
return "Travel{" + return "Travel{" +
...@@ -407,6 +428,8 @@ public class Travel { ...@@ -407,6 +428,8 @@ public class Travel {
", crossId='" + crossId + '\'' + ", crossId='" + crossId + '\'' +
", recordId='" + recordId + '\'' + ", recordId='" + recordId + '\'' +
", recordTime='" + recordTime + '\'' + ", recordTime='" + recordTime + '\'' +
", travelBeginTime='" + travelBeginTime + '\'' +
", travelEndTime='" + travelEndTime + '\'' +
", carId=" + carId + ", carId=" + carId +
", carColor='" + carColor + '\'' + ", carColor='" + carColor + '\'' +
", plate='" + plate + '\'' + ", plate='" + plate + '\'' +
......
...@@ -4,7 +4,7 @@ import com.alibaba.fastjson.JSON; ...@@ -4,7 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.zhht.irn.entity.Location; import com.zhht.irn.entity.Location;
import com.zhht.irn.entity.Travel; import com.zhht.irn.entity.Travel;
import com.zhht.irn.sink.TravelMetricSink; import com.zhht.irn.sink.TravelMetricSink;
import com.zhht.irn.source.TravelMockSource; import com.zhht.irn.utils.FileUtils;
import com.zhht.irn.utils.FlinkUtils; import com.zhht.irn.utils.FlinkUtils;
import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.datastream.DataStream;
...@@ -14,6 +14,8 @@ import org.slf4j.LoggerFactory; ...@@ -14,6 +14,8 @@ import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
import static com.zhht.irn.utils.StringUtils.isNotEmpty;
/** /**
* 旅行信息实时任务 * 旅行信息实时任务
* *
...@@ -30,31 +32,47 @@ public class TravelInfoJob { ...@@ -30,31 +32,47 @@ public class TravelInfoJob {
// 通过kafka配置文件,获取String类型的实时数据流 // 通过kafka配置文件,获取String类型的实时数据流
DataStream<String> rawStringStream = FlinkUtils.createKafkaStream(args, env); DataStream<String> rawStringStream = FlinkUtils.createKafkaStream(args, env);
// 接入数据的第一时间,要进行本地文件备份,以便后续问题排查
rawStringStream.sinkTo(FileUtils.getFileSink("./data/"));
// 开始计算旅行信息相关指标 // 开始计算旅行信息相关指标
rawStringStream.map((MapFunction<String, Travel>) s -> { rawStringStream.map((MapFunction<String, Travel>) s -> {
System.out.println(s);
// json 格式字符串转化为 Travel 实体类 @todo 不一定能正确解析 // json 格式字符串转化为 Travel 实体类 @todo 不一定能正确解析
Travel travel = JSON.parseObject(s, Travel.class); Travel travel = JSON.parseObject(s, Travel.class);
// -1 代表该值从未被处理过
double minSpeed = -1, maxSpeed = -1, totalSpeed = -1;
List<Location> locations = travel.getLocations(); int accTravel = 0;// 拥有完整开始、结束时刻的旅行记录数
String travelStart = travel.getTravelBeginTime();
String travelEnd = travel.getTravelEndTime();
int stopCount = 0; long stopTime = 0L; // 如果旅行记录没有完整的开始、结束时刻,则无法计算最高、最低、平均速度
double minSpeed = 0.0, maxSpeed = 0.0, totalSpeed = 0.0; if(isNotEmpty(travelStart) && isNotEmpty(travelEnd)) {
boolean stopped = false; // 停车状态,判断截止到处理时的停车状态 false 行驶状态(默认值),true 停车状态
for(int i = 0; i < locations.size(); i ++) { long longTravelStart = Long.parseLong(travelStart);
long longTravelEnd = Long.parseLong(travelEnd);
List<Location> locations = travel.getLocations();
for (int i = 0; i < locations.size(); i++) {
// 该位置必须在旅行的开始和结束时刻之间才能进行速度计算
long dtTimeStamp = Long.parseLong(locations.get(i).getDtTranjectory());
if (dtTimeStamp >= longTravelStart && dtTimeStamp <= longTravelEnd) {
double currentSpeed = locations.get(i).getSpeed(); double currentSpeed = locations.get(i).getSpeed();
if (i == 0) minSpeed = maxSpeed = currentSpeed; if (minSpeed == -1) minSpeed = currentSpeed;
if (maxSpeed == -1) maxSpeed = currentSpeed;
if (totalSpeed == -1) totalSpeed = 0;
// 获取最大最小值
if (currentSpeed > maxSpeed) maxSpeed = currentSpeed; if (currentSpeed > maxSpeed) maxSpeed = currentSpeed;
if (currentSpeed < minSpeed) minSpeed = currentSpeed; if (currentSpeed < minSpeed) minSpeed = currentSpeed;
// 累计速度
totalSpeed += currentSpeed; totalSpeed += currentSpeed;
accTravel ++;
}
}
}
// 停车次数的计算不限于旅行的开始时刻和结束时刻
// 实际停车为整个旅行中车辆速度低于某个下限阈值的(暂定3km/h)次数. 当车辆一直低于该下限阈值(10km/h)时, 视为一次连续的实际停车. // 实际停车为整个旅行中车辆速度低于某个下限阈值的(暂定3km/h)次数. 当车辆一直低于该下限阈值(10km/h)时, 视为一次连续的实际停车.
// 当车辆高于某个上限阈值(暂定10km/h)后, 再次低于下限阈值(暂定3km/h)时, 再记为一次实际停车 // 当车辆高于某个上限阈值(暂定10km/h)后, 再次低于下限阈值(暂定3km/h)时, 再记为一次实际停车
...@@ -63,27 +81,42 @@ public class TravelInfoJob { ...@@ -63,27 +81,42 @@ public class TravelInfoJob {
// //
// 停车次数 = 行驶状态 切换至 停车状态 的次数 // 停车次数 = 行驶状态 切换至 停车状态 的次数
// 状态 stopped 从行驶状态切换停车状态一次, 记一次实际停车数 @宇昂提供的计算思路 // 状态 stopped 从行驶状态切换停车状态一次, 记一次实际停车数 @宇昂提供的计算思路
// 车辆默认的停车状态为行驶状态
boolean stopped = false;
List<Location> locations = travel.getLocations();
long stopTime = 0;
int stopCount = 0;
for(int j = 0; j < locations.size(); j ++) {
double speed = locations.get(j).getSpeed();
if(stopped) { if(stopped) {
// 停车状态下,只关注速度上限, 10km/h // 停车状态下,只关注速度上限, 10km/h
// 如果当前速度小于 10km/h 算上一次停车的连续停车,只用累计停车时长,不用更改停车状态 // 如果当前速度小于 10km/h 算上一次停车的连续停车,只用累计停车时长,不用更改停车状态
if(currentSpeed >= 10) { if(speed >= 10) {
stopped = false; stopped = false;
} }
stopTime += (locations.get(i).getDtTranjectory().getTime() stopTime += (Long.parseLong(locations.get(j).getDtTranjectory())
- locations.get(i - 1).getDtTranjectory().getTime()); - Long.parseLong(locations.get(j - 1).getDtTranjectory()));
} else { } else {
// 行驶状态下,只关注速度下限, 3km/h, 增加停车次数 // 行驶状态下,只关注速度下限, 3km/h, 增加停车次数
if (currentSpeed < 3){ if (speed < 3){
stopped = true; stopped = true;
stopCount ++; stopCount ++;
} }
} }
} }
// 获取5个实时旅行信息指标 // 获取5个实时旅行信息指标
travel.setStopCount(stopCount); travel.setStopCount(stopCount);
travel.setStopTime(stopTime); travel.setStopTime(stopTime);
travel.setAvgSpeed(totalSpeed / locations.size()); if(totalSpeed == -1) {
travel.setAvgSpeed(-1);
} else {
travel.setAvgSpeed(totalSpeed / accTravel);
}
travel.setMaxSpeed(maxSpeed); travel.setMaxSpeed(maxSpeed);
travel.setMinSpeed(minSpeed); travel.setMinSpeed(minSpeed);
......
...@@ -21,12 +21,12 @@ public class TravelMetricSink extends RichSinkFunction<Travel> { ...@@ -21,12 +21,12 @@ public class TravelMetricSink extends RichSinkFunction<Travel> {
@Override @Override
public void open(Configuration parameters) throws Exception { public void open(Configuration parameters) throws Exception {
super.open(parameters); super.open(parameters);
// path暂定为固定值 connection = MySQLUtils.getConnection();
String path = "F:\\workspace\\ZHHT-IRN-BD-ANALYSIS\\realtime\\hologram-streaming\\src\\main\\resources\\mysql.properties";
connection = MySQLUtils.getConnection(path); //目前值考虑插入,不考虑更新
String insertSql="" + String insertSql="" +
"insert into app_travel_metric" + "insert into app_travel_metric" +
"(id, car_id, cross_id, plate, record_id, capture_time, lost_time, arrived_time, incross_time, " + "(incross_line_id, car_id, cross_id, plate, record_id, capture_time, lost_time, arrived_time, incross_time, " +
"outcross_time, away_time, stop_count, stop_time, avg_speed, min_speed, max_speed, update_time)" + "outcross_time, away_time, stop_count, stop_time, avg_speed, min_speed, max_speed, update_time)" +
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
insertPstmt = connection.prepareStatement(insertSql); insertPstmt = connection.prepareStatement(insertSql);
...@@ -38,15 +38,10 @@ public class TravelMetricSink extends RichSinkFunction<Travel> { ...@@ -38,15 +38,10 @@ public class TravelMetricSink extends RichSinkFunction<Travel> {
if(connection != null) connection.close(); if(connection != null) connection.close();
} }
/**
* 来一条数据就执行一次
*
* 1000w的数据 1000w次
*/
@Override @Override
public void invoke(Travel travel, Context context) throws Exception { public void invoke(Travel travel, Context context) throws Exception {
System.out.println("=====invoke======" + travel.getId() + "-->" + travel.getRecordId()); System.out.println("=====insert travel ======" + travel);
insertPstmt.setLong(1, travel.getId()); insertPstmt.setString(1, travel.getInCrossLineId());
insertPstmt.setLong(2, travel.getCarId()); insertPstmt.setLong(2, travel.getCarId());
insertPstmt.setString(3, travel.getCrossId()); insertPstmt.setString(3, travel.getCrossId());
insertPstmt.setString(4,travel.getPlate()); insertPstmt.setString(4,travel.getPlate());
......
package com.zhht.irn.utils;
import org.apache.flink.api.common.serialization.SimpleStringEncoder;
import org.apache.flink.connector.file.sink.FileSink;
import org.apache.flink.core.fs.Path;
import org.apache.flink.streaming.api.functions.sink.filesystem.rollingpolicies.DefaultRollingPolicy;
import java.util.concurrent.TimeUnit;
/**
* 文件工具类
*
* @author 吴延飞
* @date 2022-11-18 15:13:00
*/
public class FileUtils {
public static FileSink<String> getFileSink(String path) {
final FileSink<String> sink = FileSink
.forRowFormat(new Path(path), new SimpleStringEncoder<String>("UTF-8"))
.withRollingPolicy(
DefaultRollingPolicy.builder()
.withRolloverInterval(TimeUnit.MINUTES.toMillis(15))
.withInactivityInterval(TimeUnit.MINUTES.toMillis(5))
.withMaxPartSize(1024 * 1024 * 1024)
.build())
.build();
return sink;
}
}
...@@ -25,7 +25,7 @@ public class StringUtils { ...@@ -25,7 +25,7 @@ public class StringUtils {
* @return 是否不为空 * @return 是否不为空
*/ */
public static boolean isNotEmpty(String str) { public static boolean isNotEmpty(String str) {
return str != null && !"".equals(str); return str != null && !"".equals(str) && !str.equals("null");
} }
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment