Commit a6c1483b authored by hanbing's avatar hanbing

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

parent 11a92de9
...@@ -12,8 +12,8 @@ import java.util.Date; ...@@ -12,8 +12,8 @@ import java.util.Date;
* @date 2023/6/9 13:52 * @date 2023/6/9 13:52
*/ */
@Data @Data
@ApiModel(value = "MetricsTurnBO", description = "转向级指标查询输入参数") @ApiModel(value = "MetricsTurnAndLaneBO", description = "转向级和车道级指标查询输入参数")
public class MetricsTurnBO { public class MetricsTurnAndLaneBO {
@ApiModelProperty(value = "0正常 1失衡 2拥堵 3溢出") @ApiModelProperty(value = "0正常 1失衡 2拥堵 3溢出")
private Integer status; private Integer status;
......
...@@ -7,11 +7,12 @@ import io.swagger.annotations.ApiResponses; ...@@ -7,11 +7,12 @@ import io.swagger.annotations.ApiResponses;
import net.wanji.common.framework.rest.JsonViewObject; import net.wanji.common.framework.rest.JsonViewObject;
import net.wanji.databus.bo.CrossIdAndStartEndDateBO; import net.wanji.databus.bo.CrossIdAndStartEndDateBO;
import net.wanji.opt.bo.AbnormalDetailBO; import net.wanji.opt.bo.AbnormalDetailBO;
import net.wanji.opt.bo.MetricsTurnBO; import net.wanji.opt.bo.MetricsTurnAndLaneBO;
import net.wanji.opt.bo.SceneMetricsDetailBO; import net.wanji.opt.bo.SceneMetricsDetailBO;
import net.wanji.opt.service.impl.SceneEvaluateServiceImpl; import net.wanji.opt.service.impl.SceneEvaluateServiceImpl;
import net.wanji.opt.vo.SceneEvaluateAbnormalDetailVO; import net.wanji.opt.vo.SceneEvaluateAbnormalDetailVO;
import net.wanji.opt.vo.SceneEvaluateAbnormalDistributeVO; import net.wanji.opt.vo.SceneEvaluateAbnormalDistributeVO;
import net.wanji.opt.vo.SceneEvaluateMetricsLaneVO;
import net.wanji.opt.vo.SceneEvaluateMetricsTurnVO; import net.wanji.opt.vo.SceneEvaluateMetricsTurnVO;
import net.wanji.opt.vo.SceneMetricsDetailVO; import net.wanji.opt.vo.SceneMetricsDetailVO;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
...@@ -84,8 +85,20 @@ public class SceneEvaluateController { ...@@ -84,8 +85,20 @@ public class SceneEvaluateController {
@ApiResponses({ @ApiResponses({
@ApiResponse(code = 200, message = "OK", response = SceneEvaluateMetricsTurnVO.class), @ApiResponse(code = 200, message = "OK", response = SceneEvaluateMetricsTurnVO.class),
}) })
public JsonViewObject metricsTurn(@RequestBody MetricsTurnBO bo) throws ParseException { public JsonViewObject metricsTurn(@RequestBody MetricsTurnAndLaneBO bo) throws ParseException {
SceneEvaluateMetricsTurnVO res = sceneEvaluateService.metricsTurn(bo); SceneEvaluateMetricsTurnVO res = sceneEvaluateService.metricsTurn(bo);
return JsonViewObject.newInstance().success(res); return JsonViewObject.newInstance().success(res);
} }
@ApiOperation(value = "车道级指标", notes = "车道级指标", response = JsonViewObject.class,
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
@PostMapping(value = "/metricsLane",
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
@ApiResponses({
@ApiResponse(code = 200, message = "OK", response = SceneEvaluateMetricsLaneVO.class),
})
public JsonViewObject metricsLane(@RequestBody MetricsTurnAndLaneBO bo) throws ParseException {
SceneEvaluateMetricsLaneVO res = sceneEvaluateService.metricsLane(bo);
return JsonViewObject.newInstance().success(res);
}
} }
\ No newline at end of file
...@@ -2,10 +2,11 @@ package net.wanji.opt.service; ...@@ -2,10 +2,11 @@ package net.wanji.opt.service;
import net.wanji.databus.bo.CrossIdAndStartEndDateBO; import net.wanji.databus.bo.CrossIdAndStartEndDateBO;
import net.wanji.opt.bo.AbnormalDetailBO; import net.wanji.opt.bo.AbnormalDetailBO;
import net.wanji.opt.bo.MetricsTurnBO; import net.wanji.opt.bo.MetricsTurnAndLaneBO;
import net.wanji.opt.bo.SceneMetricsDetailBO; import net.wanji.opt.bo.SceneMetricsDetailBO;
import net.wanji.opt.vo.SceneEvaluateAbnormalDetailVO; import net.wanji.opt.vo.SceneEvaluateAbnormalDetailVO;
import net.wanji.opt.vo.SceneEvaluateAbnormalDistributeVO; import net.wanji.opt.vo.SceneEvaluateAbnormalDistributeVO;
import net.wanji.opt.vo.SceneEvaluateMetricsLaneVO;
import net.wanji.opt.vo.SceneEvaluateMetricsTurnVO; import net.wanji.opt.vo.SceneEvaluateMetricsTurnVO;
import net.wanji.opt.vo.SceneMetricsDetailVO; import net.wanji.opt.vo.SceneMetricsDetailVO;
...@@ -21,5 +22,7 @@ public interface SceneEvaluateService { ...@@ -21,5 +22,7 @@ public interface SceneEvaluateService {
List<SceneMetricsDetailVO> metricsDetail(SceneMetricsDetailBO bo) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException; List<SceneMetricsDetailVO> metricsDetail(SceneMetricsDetailBO bo) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException;
SceneEvaluateMetricsTurnVO metricsTurn(MetricsTurnBO bo) throws ParseException; SceneEvaluateMetricsTurnVO metricsTurn(MetricsTurnAndLaneBO bo) throws ParseException;
SceneEvaluateMetricsLaneVO metricsLane(MetricsTurnAndLaneBO bo) throws ParseException;
} }
...@@ -13,12 +13,10 @@ import net.wanji.databus.dao.entity.BaseCrossSchemePO; ...@@ -13,12 +13,10 @@ import net.wanji.databus.dao.entity.BaseCrossSchemePO;
import net.wanji.databus.dao.entity.CrossSectionPO; import net.wanji.databus.dao.entity.CrossSectionPO;
import net.wanji.databus.dao.mapper.*; import net.wanji.databus.dao.mapper.*;
import net.wanji.databus.dto.MetricHistDTO; import net.wanji.databus.dto.MetricHistDTO;
import net.wanji.databus.po.CrossDataHistPO; import net.wanji.databus.po.*;
import net.wanji.databus.po.CrossDirDataHistPO;
import net.wanji.databus.po.CrossTurnDataHistPO;
import net.wanji.feign.service.ControlFeignClients; import net.wanji.feign.service.ControlFeignClients;
import net.wanji.opt.bo.AbnormalDetailBO; import net.wanji.opt.bo.AbnormalDetailBO;
import net.wanji.opt.bo.MetricsTurnBO; import net.wanji.opt.bo.MetricsTurnAndLaneBO;
import net.wanji.opt.bo.SceneMetricsDetailBO; import net.wanji.opt.bo.SceneMetricsDetailBO;
import net.wanji.opt.service.SceneEvaluateService; import net.wanji.opt.service.SceneEvaluateService;
import net.wanji.opt.vo.*; import net.wanji.opt.vo.*;
...@@ -300,7 +298,7 @@ public class SceneEvaluateServiceImpl implements SceneEvaluateService { ...@@ -300,7 +298,7 @@ public class SceneEvaluateServiceImpl implements SceneEvaluateService {
} }
@Override @Override
public SceneEvaluateMetricsTurnVO metricsTurn(MetricsTurnBO bo) throws ParseException { public SceneEvaluateMetricsTurnVO metricsTurn(MetricsTurnAndLaneBO bo) throws ParseException {
Integer status = bo.getStatus(); Integer status = bo.getStatus();
String crossId = bo.getCrossId(); String crossId = bo.getCrossId();
Integer dir = bo.getDir(); Integer dir = bo.getDir();
...@@ -347,6 +345,200 @@ public class SceneEvaluateServiceImpl implements SceneEvaluateService { ...@@ -347,6 +345,200 @@ public class SceneEvaluateServiceImpl implements SceneEvaluateService {
return res; return res;
} }
@Override
public SceneEvaluateMetricsLaneVO metricsLane(MetricsTurnAndLaneBO 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<CrossLaneDataHistPOExt> poExtList =
crossLaneDataHistMapper.selectByCrossIdAndDir(crossId, dir, endTimeStamp, startTimeStamp);
Map<Integer, List<CrossLaneDataHistPOExt>> groupedBySort = poExtList.stream()
.collect(Collectors.groupingBy(CrossLaneDataHistPOExt::getSort));
SceneEvaluateMetricsLaneVO res = new SceneEvaluateMetricsLaneVO();
// 获取延误
double v = poExtList.stream()
.mapToInt(CrossLaneDataHistPOExt::getDelayTime)
.average()
.orElse(0);
Integer delayTime = (int)v;
Integer level = calcCongestionLevel(delayTime);
res.setLevel(level);
List<SceneEvaluateMetricsLaneVO.LaneListElement> elementList = new ArrayList<>();
for (Map.Entry<Integer, List<CrossLaneDataHistPOExt>> entry : groupedBySort.entrySet()) {
SceneEvaluateMetricsLaneVO.LaneListElement element = buildLaneListElement(entry, status);
elementList.add(element);
}
res.setLaneList(elementList);
return res;
}
private SceneEvaluateMetricsLaneVO.LaneListElement buildLaneListElement(
Map.Entry<Integer, List<CrossLaneDataHistPOExt>> entry, Integer status) {
SceneEvaluateMetricsLaneVO.LaneListElement element = new SceneEvaluateMetricsLaneVO.LaneListElement();
Integer sort = entry.getKey();
element.setSort(sort);
List<CrossLaneDataHistPOExt> poExtList = entry.getValue();
// 获取流量
OptionalDouble optionalAverageFlow = poExtList.stream()
.filter(Objects::nonNull)
.mapToInt(CrossLaneDataHistPOExt::getFlow)
.average();
int averageFlow = 0;
if (optionalAverageFlow.isPresent()) {
averageFlow = (int) Math.round(optionalAverageFlow.getAsDouble());
}
element.setFlow(averageFlow);
// 动态指标
element.setMetricsMap(buildLaneMetricMap(poExtList, status));
return element;
}
private Map<String, Integer> buildLaneMetricMap(List<CrossLaneDataHistPOExt> poExtList, Integer status) {
Map<String, Integer> res = new HashMap<>();
if (Objects.equals(status, CrossStatusEnum.CONGESTION.getCode())) {
buildLaneCongestionRes(res, poExtList);
} else if (Objects.equals(status, CrossStatusEnum.UNBALANCE.getCode())) {
buildLaneUnbalanceRes(res, poExtList);
} else if (Objects.equals(status, CrossStatusEnum.SPILLOVER.getCode())) {
buildLaneSpilloverRes(res, poExtList);
}
return res;
}
private void buildLaneSpilloverRes(Map<String, Integer> res, List<CrossLaneDataHistPOExt> poExtList) {
// 最大排队长度
double maxQueueLength = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getQueueLength)
.max()
.orElse(0.0);
int maxQueueLengthInt = (int) (Math.round(maxQueueLength));
res.put(StrategyAndMetricsEnum.Metrics.MAX_QUEUE_LENGTH.getCode(), maxQueueLengthInt);
// 平均延误
double maxDelayTime = poExtList.stream()
.mapToInt(CrossLaneDataHistPOExt::getDelayTime)
.average()
.orElse(0.0);
int maxDelayTimeInt = (int) (Math.round(maxDelayTime));
res.put(StrategyAndMetricsEnum.Metrics.AVERAGE_DELAY.getCode(), maxDelayTimeInt);
// 停车次数
double maxStopTimes = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getStopTimes)
.average()
.orElse(0.0);
int maxStopTimesInt = (int) (Math.round(maxStopTimes));
res.put(StrategyAndMetricsEnum.Metrics.STOP_TIMES.getCode(), maxStopTimesInt);
// 溢流率
double maxEffusionRate = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getEffusionRate)
.average()
.orElse(0.0);
int maxEffusionRateInt = (int) (Math.round(maxEffusionRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.EFFUSION_RATE.getCode(), maxEffusionRateInt);
}
private void buildLaneUnbalanceRes(Map<String, Integer> res, List<CrossLaneDataHistPOExt> poExtList) {
// 不停车通过率
double maxNoStopRate = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getNoStopRate)
.average()
.orElse(0.0);
int maxNoStopRateInt = (int) (Math.round(maxNoStopRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.NO_STOP_RATE.getCode(), maxNoStopRateInt);
// N次停车通过率
double maxOneStopRate = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getOneStopRate)
.average()
.orElse(0.0);
int maxOneStopRateInt = (int) (Math.round(maxOneStopRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.STOP_RATE.getCode(), maxOneStopRateInt);
// 空放次数-绿灯有效利用率小于80%
long emptyDischarges = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getGreenLightEfficiency)
.filter(efficiency -> efficiency < 0.8)
.count();
res.put(StrategyAndMetricsEnum.Metrics.EMPTY_DISCHARGES.getCode(), (int)emptyDischarges);
// 绿灯有效利用率
double maxGreenLightEfficiency = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getGreenLightEfficiency)
.average()
.orElse(0.0);
int maxGreenLightEfficiencyInt = (int) (Math.round(maxGreenLightEfficiency * 100));
res.put(StrategyAndMetricsEnum.Metrics.GREEN_LIGHT_EFFICIENCY.getCode(), maxGreenLightEfficiencyInt);
}
private void buildLaneCongestionRes(Map<String, Integer> res, List<CrossLaneDataHistPOExt> poExtList) {
// N次停车通过率
double maxOneStopRate = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getOneStopRate)
.average()
.orElse(0.0);
int maxOneStopRateInt = (int) (Math.round(maxOneStopRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.STOP_RATE.getCode(), maxOneStopRateInt);
// 平均速度
double maxSpeed = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getSpeed)
.average()
.orElse(0.0);
int maxSpeedInt = (int) (Math.round(maxSpeed));
res.put(StrategyAndMetricsEnum.Metrics.AVERAGE_SPEED.getCode(), maxSpeedInt);
// 平均延误
double maxDelayTime = poExtList.stream()
.mapToInt(CrossLaneDataHistPOExt::getDelayTime)
.average()
.orElse(0.0);
int maxDelayTimeInt = (int) (Math.round(maxDelayTime));
res.put(StrategyAndMetricsEnum.Metrics.AVERAGE_DELAY.getCode(), maxDelayTimeInt);
// 停车次数
double maxStopTimes = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getStopTimes)
.average()
.orElse(0.0);
int maxStopTimesInt = (int) (Math.round(maxStopTimes));
res.put(StrategyAndMetricsEnum.Metrics.STOP_TIMES.getCode(), maxStopTimesInt);
// 最大排队长度
double maxQueueLength = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getQueueLength)
.max()
.orElse(0.0);
int maxQueueLengthInt = (int) (Math.round(maxQueueLength));
res.put(StrategyAndMetricsEnum.Metrics.MAX_QUEUE_LENGTH.getCode(), maxQueueLengthInt);
// 溢流率
double maxEffusionRate = poExtList.stream()
.mapToDouble(CrossLaneDataHistPOExt::getEffusionRate)
.average()
.orElse(0.0);
int maxEffusionRateInt = (int) (Math.round(maxEffusionRate * 100));
res.put(StrategyAndMetricsEnum.Metrics.EFFUSION_RATE.getCode(), maxEffusionRateInt);
}
private SceneEvaluateMetricsTurnVO.TurnListElement buildTurnListElement( private SceneEvaluateMetricsTurnVO.TurnListElement buildTurnListElement(
Map.Entry<String, List<CrossTurnDataHistPO>> entry, Integer status) { Map.Entry<String, List<CrossTurnDataHistPO>> entry, Integer status) {
SceneEvaluateMetricsTurnVO.TurnListElement element = new SceneEvaluateMetricsTurnVO.TurnListElement(); SceneEvaluateMetricsTurnVO.TurnListElement element = new SceneEvaluateMetricsTurnVO.TurnListElement();
......
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.List;
import java.util.Map;
/**
* @author Kent HAN
* @date 2023/2/9 8:38
*/
@Data
@NoArgsConstructor
@ApiModel(value = "SceneEvaluateMetricsLaneVO", description = "车道级指标")
public class SceneEvaluateMetricsLaneVO {
// todo 目前只有拥堵,后期扩展考虑增加字段
@ApiModelProperty(value = "等级",notes = "0畅通 1轻度 2中度 3严重")
private Integer level ;
@ApiModelProperty(value = "车道指标")
private List<LaneListElement> laneList;
@NoArgsConstructor
@Data
public static class LaneListElement {
@ApiModelProperty(value = "车道序号")
private Integer sort;
@ApiModelProperty(value = "流量(单位:pcu/5min)")
private Integer flow;
// 可变指标数据
@JsonIgnore
private Map<String, Integer> metricsMap;
@JsonAnyGetter
public Map<String, Integer> any() {
return metricsMap;
}
}
}
...@@ -3,6 +3,7 @@ package net.wanji.databus.dao.mapper; ...@@ -3,6 +3,7 @@ package net.wanji.databus.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.wanji.databus.dto.MetricHistDTO; import net.wanji.databus.dto.MetricHistDTO;
import net.wanji.databus.po.CrossLaneDataHistPO; import net.wanji.databus.po.CrossLaneDataHistPO;
import net.wanji.databus.po.CrossLaneDataHistPOExt;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
...@@ -18,4 +19,5 @@ public interface CrossLaneDataHistMapper extends BaseMapper<CrossLaneDataHistPO> ...@@ -18,4 +19,5 @@ public interface CrossLaneDataHistMapper extends BaseMapper<CrossLaneDataHistPO>
void insertBatch(@Param("list") List<CrossLaneDataHistPO> list); void insertBatch(@Param("list") List<CrossLaneDataHistPO> list);
void deleteBatch(@Param("list") Collection<String> crossIds); void deleteBatch(@Param("list") Collection<String> crossIds);
List<MetricHistDTO> selectMetricHistDTO(String crossId, int startStamp, int endStamp); List<MetricHistDTO> selectMetricHistDTO(String crossId, int startStamp, int endStamp);
List<CrossLaneDataHistPOExt> selectByCrossIdAndDir(String crossId, Integer dir, int endTimeStamp, int startTimeStamp);
} }
package net.wanji.databus.po;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author duanruiming
* @date 2023/03/12 20:53
*/
@Data
public class CrossLaneDataHistPOExt extends CrossLaneDataRealTimePO{
// 方向
private Integer dir;
// 车道序号,从左车道开始编号11、12、13...
private Integer sort;
}
...@@ -63,4 +63,15 @@ ...@@ -63,4 +63,15 @@
and batch_time <![CDATA[ <= ]]> #{endStamp} and batch_time <![CDATA[ <= ]]> #{endStamp}
order by batch_time order by batch_time
</select> </select>
<select id="selectByCrossIdAndDir" resultType="net.wanji.databus.po.CrossLaneDataHistPOExt">
SELECT *
FROM t_base_lane_info
JOIN t_lane_data_hist ON t_base_lane_info.id = t_lane_data_hist.id
WHERE t_base_lane_info.cross_id = #{crossId}
AND t_base_lane_info.type = 2
AND t_base_lane_info.dir = #{dir}
AND batch_time <![CDATA[ >= ]]> #{startTimeStamp}
AND batch_time <![CDATA[ <= ]]> #{endTimeStamp};
</select>
</mapper> </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