Commit 11a92de9 authored by hanbing's avatar hanbing

[add] 新信号评价-运行评价-详细指标查询,转向级指标

parent 1726c58b
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "MetricsTurnBO", description = "转向级指标查询输入参数")
public class MetricsTurnBO {
@ApiModelProperty(value = "0正常 1失衡 2拥堵 3溢出")
private Integer status;
@ApiModelProperty(value = "路口ID", required = true)
private String crossId;
@ApiModelProperty(value = "进口方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北", required = true)
private Integer dir;
@ApiModelProperty(value = "时间 HH:mm", required = true)
private String hourMinute;
@ApiModelProperty(value = "日期 yyyy-MM-dd")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date day;
}
......@@ -7,10 +7,12 @@ import io.swagger.annotations.ApiResponses;
import net.wanji.common.framework.rest.JsonViewObject;
import net.wanji.databus.bo.CrossIdAndStartEndDateBO;
import net.wanji.opt.bo.AbnormalDetailBO;
import net.wanji.opt.bo.MetricsTurnBO;
import net.wanji.opt.bo.SceneMetricsDetailBO;
import net.wanji.opt.service.impl.SceneEvaluateServiceImpl;
import net.wanji.opt.vo.SceneEvaluateAbnormalDetailVO;
import net.wanji.opt.vo.SceneEvaluateAbnormalDistributeVO;
import net.wanji.opt.vo.SceneEvaluateMetricsTurnVO;
import net.wanji.opt.vo.SceneMetricsDetailVO;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
......@@ -62,7 +64,7 @@ public class SceneEvaluateController {
return JsonViewObject.newInstance().success(res);
}
@ApiOperation(value = "详细指标查询", notes = "详细指标查询", response = JsonViewObject.class,
@ApiOperation(value = "曲线图", notes = "曲线图", response = JsonViewObject.class,
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
@PostMapping(value = "/metricsDetail",
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
......@@ -74,4 +76,16 @@ public class SceneEvaluateController {
List<SceneMetricsDetailVO> res = sceneEvaluateService.metricsDetail(bo);
return JsonViewObject.newInstance().success(res);
}
@ApiOperation(value = "转向级指标", notes = "转向级指标", response = JsonViewObject.class,
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
@PostMapping(value = "/metricsTurn",
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
@ApiResponses({
@ApiResponse(code = 200, message = "OK", response = SceneEvaluateMetricsTurnVO.class),
})
public JsonViewObject metricsTurn(@RequestBody MetricsTurnBO bo) throws ParseException {
SceneEvaluateMetricsTurnVO res = sceneEvaluateService.metricsTurn(bo);
return JsonViewObject.newInstance().success(res);
}
}
\ No newline at end of file
......@@ -2,9 +2,11 @@ package net.wanji.opt.service;
import net.wanji.databus.bo.CrossIdAndStartEndDateBO;
import net.wanji.opt.bo.AbnormalDetailBO;
import net.wanji.opt.bo.MetricsTurnBO;
import net.wanji.opt.bo.SceneMetricsDetailBO;
import net.wanji.opt.vo.SceneEvaluateAbnormalDetailVO;
import net.wanji.opt.vo.SceneEvaluateAbnormalDistributeVO;
import net.wanji.opt.vo.SceneEvaluateMetricsTurnVO;
import net.wanji.opt.vo.SceneMetricsDetailVO;
import java.lang.reflect.InvocationTargetException;
......@@ -18,4 +20,6 @@ public interface SceneEvaluateService {
SceneEvaluateAbnormalDetailVO abnormalDetail(AbnormalDetailBO abnormalDetailBO) throws ParseException;
List<SceneMetricsDetailVO> metricsDetail(SceneMetricsDetailBO bo) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException;
SceneEvaluateMetricsTurnVO metricsTurn(MetricsTurnBO bo) throws ParseException;
}
......@@ -15,8 +15,10 @@ import net.wanji.databus.dao.mapper.*;
import net.wanji.databus.dto.MetricHistDTO;
import net.wanji.databus.po.CrossDataHistPO;
import net.wanji.databus.po.CrossDirDataHistPO;
import net.wanji.databus.po.CrossTurnDataHistPO;
import net.wanji.feign.service.ControlFeignClients;
import net.wanji.opt.bo.AbnormalDetailBO;
import net.wanji.opt.bo.MetricsTurnBO;
import net.wanji.opt.bo.SceneMetricsDetailBO;
import net.wanji.opt.service.SceneEvaluateService;
import net.wanji.opt.vo.*;
......@@ -297,6 +299,197 @@ public class SceneEvaluateServiceImpl implements SceneEvaluateService {
return res;
}
@Override
public SceneEvaluateMetricsTurnVO metricsTurn(MetricsTurnBO bo) throws ParseException {
Integer status = bo.getStatus();
String crossId = bo.getCrossId();
Integer dir = bo.getDir();
Date day = bo.getDay();
String hourMinute = bo.getHourMinute();
// 构造开始时间结束时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String todayDateString = sdf.format(day); // Get today's date
String startTimeString = todayDateString + " " + hourMinute + ":00";
Date startTime = dateTimeFormatter.parse(startTimeString);
Calendar cal = Calendar.getInstance();
cal.setTime(startTime);
cal.add(Calendar.MINUTE, 4);
cal.add(Calendar.SECOND, 59);
Date endTime = cal.getTime();
int startTimeStamp = (int) (startTime.getTime() / 1000);
int endTimeStamp = (int) (endTime.getTime() / 1000);
// 查询该时段内转向级别数据
List<CrossTurnDataHistPO> crossTurnDataHistPOList =
crossTurnDataHistMapper.selectByCrossIdAndDir(crossId, dir, endTimeStamp, startTimeStamp);
Map<String, List<CrossTurnDataHistPO>> groupedByTurn = crossTurnDataHistPOList.stream()
.collect(Collectors.groupingBy(CrossTurnDataHistPO::getTurnType));
SceneEvaluateMetricsTurnVO res = new SceneEvaluateMetricsTurnVO();
// 获取延误
double v = crossTurnDataHistPOList.stream()
.mapToInt(CrossTurnDataHistPO::getDelayTime)
.average()
.orElse(0);
Integer delayTime = (int)v;
Integer level = calcCongestionLevel(delayTime);
res.setLevel(level);
List<SceneEvaluateMetricsTurnVO.TurnListElement> elementList = new ArrayList<>();
for (Map.Entry<String, List<CrossTurnDataHistPO>> entry : groupedByTurn.entrySet()) {
SceneEvaluateMetricsTurnVO.TurnListElement element = buildTurnListElement(entry, status);
elementList.add(element);
}
res.setTurnList(elementList);
return res;
}
private SceneEvaluateMetricsTurnVO.TurnListElement buildTurnListElement(
Map.Entry<String, List<CrossTurnDataHistPO>> entry, Integer status) {
SceneEvaluateMetricsTurnVO.TurnListElement element = new SceneEvaluateMetricsTurnVO.TurnListElement();
String turn = entry.getKey();
element.setTurn(turn);
List<CrossTurnDataHistPO> poList = entry.getValue();
// 获取流量
OptionalDouble optionalAverageFlow = poList.stream()
.filter(Objects::nonNull)
.mapToInt(CrossTurnDataHistPO::getFlow)
.average();
int averageFlow = 0;
if (optionalAverageFlow.isPresent()) {
averageFlow = (int) Math.round(optionalAverageFlow.getAsDouble());
}
element.setFlow(averageFlow);
// 动态指标
element.setMetricsMap(buildTurnMetricMap(poList, status));
return element;
}
private Map<String, Integer> buildTurnMetricMap(List<CrossTurnDataHistPO> poList, Integer status) {
Map<String, Integer> res = new HashMap<>();
if (Objects.equals(status, CrossStatusEnum.CONGESTION.getCode())) {
buildTurnCongestionRes(res, poList);
} else if (Objects.equals(status, CrossStatusEnum.UNBALANCE.getCode())) {
buildTurnUnbalanceRes(res, poList);
} else if (Objects.equals(status, CrossStatusEnum.SPILLOVER.getCode())) {
buildTurnSpilloverRes(res, poList);
}
return res;
}
private void buildTurnSpilloverRes(Map<String, Integer> res, List<CrossTurnDataHistPO> poList) {
// 最大排队长度
double maxQueueLength = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getQueueLength)
.max()
.orElse(0.0);
int maxQueueLengthInt = (int) (Math.round(maxQueueLength));
res.put(StrategyAndMetricsEnum.Metrics.MAX_QUEUE_LENGTH.getCode(), maxQueueLengthInt);
// 平均延误
double maxDelayTime = poList.stream()
.mapToInt(CrossTurnDataHistPO::getDelayTime)
.average()
.orElse(0.0);
int maxDelayTimeInt = (int) (Math.round(maxDelayTime));
res.put(StrategyAndMetricsEnum.Metrics.AVERAGE_DELAY.getCode(), maxDelayTimeInt);
// 停车次数
double maxStopTimes = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getStopTimes)
.average()
.orElse(0.0);
int maxStopTimesInt = (int) (Math.round(maxStopTimes));
res.put(StrategyAndMetricsEnum.Metrics.STOP_TIMES.getCode(), maxStopTimesInt);
// 溢流率
double maxEffusionRate = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getEffusionRate)
.average()
.orElse(0.0);
int maxEffusionRateInt = (int) (Math.round(maxEffusionRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.EFFUSION_RATE.getCode(), maxEffusionRateInt);
}
private void buildTurnUnbalanceRes(Map<String, Integer> res, List<CrossTurnDataHistPO> poList) {
// 不停车通过率
double maxNoStopRate = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getNoStopRate)
.average()
.orElse(0.0);
int maxNoStopRateInt = (int) (Math.round(maxNoStopRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.NO_STOP_RATE.getCode(), maxNoStopRateInt);
// N次停车通过率
double maxOneStopRate = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getOneStopRate)
.average()
.orElse(0.0);
int maxOneStopRateInt = (int) (Math.round(maxOneStopRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.STOP_RATE.getCode(), maxOneStopRateInt);
// 空放次数-绿灯有效利用率小于80%
long emptyDischarges = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getGreenLightEfficiency)
.filter(efficiency -> efficiency < 0.8)
.count();
res.put(StrategyAndMetricsEnum.Metrics.EMPTY_DISCHARGES.getCode(), (int)emptyDischarges);
// 绿灯有效利用率
double maxGreenLightEfficiency = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getGreenLightEfficiency)
.average()
.orElse(0.0);
int maxGreenLightEfficiencyInt = (int) (Math.round(maxGreenLightEfficiency * 100));
res.put(StrategyAndMetricsEnum.Metrics.GREEN_LIGHT_EFFICIENCY.getCode(), maxGreenLightEfficiencyInt);
}
private void buildTurnCongestionRes(Map<String, Integer> res, List<CrossTurnDataHistPO> poList) {
// N次停车通过率
double maxOneStopRate = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getOneStopRate)
.average()
.orElse(0.0);
int maxOneStopRateInt = (int) (Math.round(maxOneStopRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.STOP_RATE.getCode(), maxOneStopRateInt);
// 平均速度
double maxSpeed = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getSpeed)
.average()
.orElse(0.0);
int maxSpeedInt = (int) (Math.round(maxSpeed));
res.put(StrategyAndMetricsEnum.Metrics.AVERAGE_SPEED.getCode(), maxSpeedInt);
// 平均延误
double maxDelayTime = poList.stream()
.mapToInt(CrossTurnDataHistPO::getDelayTime)
.average()
.orElse(0.0);
int maxDelayTimeInt = (int) (Math.round(maxDelayTime));
res.put(StrategyAndMetricsEnum.Metrics.AVERAGE_DELAY.getCode(), maxDelayTimeInt);
// 停车次数
double maxStopTimes = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getStopTimes)
.average()
.orElse(0.0);
int maxStopTimesInt = (int) (Math.round(maxStopTimes));
res.put(StrategyAndMetricsEnum.Metrics.STOP_TIMES.getCode(), maxStopTimesInt);
// 最大排队长度
double maxQueueLength = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getQueueLength)
.max()
.orElse(0.0);
int maxQueueLengthInt = (int) (Math.round(maxQueueLength));
res.put(StrategyAndMetricsEnum.Metrics.MAX_QUEUE_LENGTH.getCode(), maxQueueLengthInt);
// 溢流率
double maxEffusionRate = poList.stream()
.mapToDouble(CrossTurnDataHistPO::getEffusionRate)
.average()
.orElse(0.0);
int maxEffusionRateInt = (int) (Math.round(maxEffusionRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.EFFUSION_RATE.getCode(), maxEffusionRateInt);
}
private List<SceneMetricsDetailVO> buildMetricsList(
List<MetricHistDTO> metricHistDTOList, Integer minutes, List<String> metricCodes) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List<SceneMetricsDetailVO> res = new ArrayList<>();
......
package net.wanji.opt.vo;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author Kent HAN
* @date 2023/2/9 8:38
*/
@Data
@NoArgsConstructor
@ApiModel(value = "SceneEvaluateMetricsTurnVO", description = "转向级指标")
public class SceneEvaluateMetricsTurnVO {
// todo 目前只有拥堵,后期扩展考虑增加字段
@ApiModelProperty(value = "等级",notes = "0畅通 1轻度 2中度 3严重")
private Integer level ;
@ApiModelProperty(value = "转向指标")
private List<TurnListElement> turnList;
@NoArgsConstructor
@Data
public static class TurnListElement {
@ApiModelProperty(value = "转向:u掉头;l左转;s直行;r右转")
private String turn;
@ApiModelProperty(value = "流量(单位:pcu/5min)")
private Integer flow;
// 可变指标数据
@JsonIgnore
private Map<String, Integer> metricsMap;
@JsonAnyGetter
public Map<String, Integer> any() {
return metricsMap;
}
}
}
......@@ -26,4 +26,6 @@ public interface CrossTurnDataHistMapper extends BaseMapper<CrossTurnDataHistPO>
List<MetricHistDTO> selectMetricHistDTO(String crossId, int startStamp, int endStamp);
List<CrossTurnDataHistPOExt> selectByMetrics(String crossId, int dirInt, String turnType, int startTimeStamp, int endTimeStamp, List<String> laneIds);
List<CrossTurnDataHistPO> selectByCrossIdAndDir(String crossId, Integer dir, long endTimeStamp, long startTimeStamp);
}
......@@ -117,5 +117,13 @@
AND batch_time <![CDATA[ >= ]]> #{startTimeStamp}
</select>
<select id="selectByCrossIdAndDir" resultType="net.wanji.databus.po.CrossTurnDataHistPO">
select <include refid="Base_Column_List"/>
from t_cross_turn_data_hist
where cross_id = #{crossId} and in_dir = #{dir}
and batch_time <![CDATA[ <= ]]> #{endTimeStamp}
and batch_time <![CDATA[ >= ]]> #{startTimeStamp}
</select>
</mapper>
\ No newline at end of file
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