Commit 67f79247 authored by duanruiming's avatar duanruiming

[add] 优化模块引入feign远程调用;实时检测-路口失衡优化

parent 644f8018
......@@ -19,6 +19,11 @@
</properties>
<dependencies>
<dependency>
<groupId>net.wanji</groupId>
<artifactId>signal-feign-service</artifactId>
<version>0.0.2</version>
</dependency>
<dependency>
<groupId>net.wanji</groupId>
<artifactId>wj-common</artifactId>
......@@ -174,7 +179,8 @@
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>${mybatis.generator.version}</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/mybatis-generator/generatorConfig.xml</configurationFile>
<configurationFile>${basedir}/src/main/resources/mybatis-generator/generatorConfig.xml
</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
......
......@@ -3,7 +3,7 @@ package net.wanji.opt.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
import java.util.Objects;
/**
* @author hfx
......@@ -13,52 +13,78 @@ import java.util.List;
@Data
public class CrossTurnDataRealtimeDTO {
@ApiModelProperty(name = "转向ID",notes = "")
@ApiModelProperty(name = "转向ID", notes = "")
private String turnId;
@ApiModelProperty(name = "转向类型:u掉头;l左转;s直行;r右转;",notes = "")
@ApiModelProperty(name = "转向类型:u掉头;l左转;s直行;r右转;", notes = "")
private Integer turnType;
@ApiModelProperty(name = "驶入方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北",notes = "")
@ApiModelProperty(name = "驶入方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北", notes = "")
private Integer inDir;
@ApiModelProperty(name = "驶出方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北",notes = "")
@ApiModelProperty(name = "驶出方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北", notes = "")
private Integer outDir;
@ApiModelProperty(name = "路口ID",notes = "")
@ApiModelProperty(name = "路口ID", notes = "")
private String crossId;
@ApiModelProperty(name = "交通流量(辆)",notes = "")
@ApiModelProperty(name = "交通流量(辆)", notes = "")
private Integer flow;
@ApiModelProperty(name = "平均速度(km/h)",notes = "")
@ApiModelProperty(name = "平均速度(km/h)", notes = "")
private Double speed;
@ApiModelProperty(name = "驶入速度(km/h)",notes = "")
@ApiModelProperty(name = "驶入速度(km/h)", notes = "")
private Double inSpeed;
@ApiModelProperty(name = "驶出速度(km/h)",notes = "")
@ApiModelProperty(name = "驶出速度(km/h)", notes = "")
private Double outSpeed;
@ApiModelProperty(name = "排队长度(米)",notes = "")
@ApiModelProperty(name = "排队长度(米)", notes = "")
private Double queueLength;
@ApiModelProperty(name = "停车次数(次)",notes = "")
@ApiModelProperty(name = "停车次数(次)", notes = "")
private Double stopTimes;
@ApiModelProperty(name = "延误时间(秒)",notes = "")
@ApiModelProperty(name = "延误时间(秒)", notes = "")
private Integer delayTime;
@ApiModelProperty(name = "饱和度",notes = "")
@ApiModelProperty(name = "饱和度", notes = "")
private Double sturation;
@ApiModelProperty(name = "车头间距(米)",notes = "")
@ApiModelProperty(name = "车头间距(米)", notes = "")
private Double vehheadDist;
@ApiModelProperty(name = "车头时距(秒)",notes = "")
@ApiModelProperty(name = "车头时距(秒)", notes = "")
private Double vehheadTime;
@ApiModelProperty(name = "数据批次(10位时间戳)",notes = "")
@ApiModelProperty(name = "数据批次(10位时间戳)", notes = "")
private Integer batchTime;
private Double passTime;
/**
* 计算路口各转向所需的通行时长(秒)
* 相位的通行时长需大于等于最小绿灯时长,并且小于等于最大绿灯时长
* 车头间距(米):车间据如果不在20和7之间取9(默认9)
* 车头时距(秒):车时距如果不在2和5之间取2.8(默认2.8)
* 排队车辆 = 排队长度 / 车头间距
* 通行时长 = 排队车辆 * 车头时距
*/
public Double calPassTime(CrossTurnDataRealtimeDTO entity) {
Double currentVehheadDist = entity.getVehheadDist();
if (Objects.isNull(currentVehheadDist) || currentVehheadDist > 20 || currentVehheadDist < 7) {
currentVehheadDist = 9.0;
}
Double currentVehheadTime = entity.getVehheadTime();
if (Objects.isNull(currentVehheadTime) || currentVehheadTime > 5 || currentVehheadTime < 2) {
currentVehheadTime = 2.8;
}
// 排队车辆
Double queuedVehicles = entity.getQueueLength() / currentVehheadDist;
// 通行时长
Double calPassTime = queuedVehicles * currentVehheadTime;
return calPassTime;
}
}
......@@ -7,11 +7,16 @@ import net.wanji.common.enums.TurnConvertEnum;
import net.wanji.common.enums.WeekEnum;
import net.wanji.common.framework.Constants;
import net.wanji.common.utils.tool.DateUtil;
import net.wanji.common.utils.tool.StringUtils;
import net.wanji.feign.pojo.result.JsonViewObject;
import net.wanji.feign.pojo.vo.SignalStatusVO;
import net.wanji.feign.service.UtcFeignClients;
import net.wanji.opt.dto.*;
import net.wanji.opt.service.CrossOptimizeService;
import net.wanji.opt.service.CrossSchedulesService;
import net.wanji.opt.service.CrossSchemeService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;
......@@ -32,9 +37,12 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
@Resource
CrossSchedulesService crossSchedulesService;
static Set<String> CROSS_OPT = new HashSet<>(); // 记录已优化的路口
@Resource
UtcFeignClients utcFeignClients;
static Set<String> CROSS_OPT = new HashSet<>(); // 记录已优化的路口
@Override
public String realtimeOptimize(List<CrossDataRealtimeDTO> abnormalCrossList) {
try {
......@@ -45,15 +53,15 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
Map<Integer, List<CrossDataRealtimeDTO>> crossDataMap = abnormalCrossList.stream().collect(Collectors.groupingBy(CrossDataRealtimeDTO::getStatus));
abnormalCrossList = crossDataMap.get(CrossStatusEnum.SPILLOVER.getCode()); // 溢出
if(abnormalCrossList != null && !abnormalCrossList.isEmpty()) {
if (abnormalCrossList != null && !abnormalCrossList.isEmpty()) {
}
abnormalCrossList = crossDataMap.get(CrossStatusEnum.CONGESTION.getCode()); // 拥堵
if(abnormalCrossList != null && !abnormalCrossList.isEmpty()) {
if (abnormalCrossList != null && !abnormalCrossList.isEmpty()) {
}
abnormalCrossList = crossDataMap.get(CrossStatusEnum.UNBALANCE.getCode()); // 失衡
if(abnormalCrossList != null && !abnormalCrossList.isEmpty()) {
if (abnormalCrossList != null && !abnormalCrossList.isEmpty()) {
unbalanceOpt(abnormalCrossList, turnDataRealtime, phaseMap);
}
} catch (Exception e) {
......@@ -65,60 +73,183 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
/**
* 路口失衡优化
*
* @param abnormalCrossList 失衡路口实时数据
* @param turnDataRealtime 路口转向实时数据
* @param turnDataRealtimeMap 路口转向实时数据
* @param phaseMap 路口相位配时数据
*/
private void unbalanceOpt(List<CrossDataRealtimeDTO> abnormalCrossList, Map<String, List<CrossTurnDataRealtimeDTO>> turnDataRealtime,
Map<String, CrossPhaseDTO> phaseMap) {
private void unbalanceOpt(List<CrossDataRealtimeDTO> abnormalCrossList, Map<String, List<CrossTurnDataRealtimeDTO>> turnDataRealtimeMap,
Map<String, CrossPhaseDTO> phaseMap) throws Exception {
String crossId;
for(CrossDataRealtimeDTO cross : abnormalCrossList) {
for (CrossDataRealtimeDTO cross : abnormalCrossList) {
crossId = cross.getCrossId();
// 判断信号机是否在线 todo 是否某些故障也不需要优化
// 判断当前路口是否存在特殊控制操作
if (isOffLineOrSpecialControlMode(crossId)) {
continue;
}
// 判断当前路口是否已优化
if(CROSS_OPT.contains(crossId)) {
if (CROSS_OPT.contains(crossId)) {
continue;
}
// 判断路口是否是绿波
// 判断信号机是否在线
// 计算路口各转向所需的通行时长(秒)
List<CrossTurnDataRealtimeDTO> crossTurnDataRealtimeDTOS = turnDataRealtimeMap.get(crossId);
crossTurnDataRealtimeDTOS.forEach(item -> item.setPassTime(item.calPassTime(item)));
// 计算路口各相位调整时长(秒)
Map<String, Integer> phaseTimeOptResultMap = new HashMap<>();
Map<Integer, String> timeOffsetPhaseMap = getTimeOffsetPhaseMap(phaseMap, crossTurnDataRealtimeDTOS);
if (isSplitPhase(timeOffsetPhaseMap)) {
} else {
phaseTimeOptResultMap = getPhaseTimeOptMap(timeOffsetPhaseMap);
}
// 相位优化总的可加可减时间,
/**
*
* 结果输出
* 路口编号、计划号、方案号、相位号、原相位时长、优化后相位时长、调整时长
*/
// 方案优化下发
// 记录已优化的路口
CROSS_OPT.add(crossId);
}
}
private static Map<String, Integer> getPhaseTimeOptMap(Map<Integer, String> timeOffsetPhaseMap) {
HashMap<String, Integer> phaseTimeOptResultMap = new HashMap<>();
// key:相位号 value:最大可加减时间
Map<String, Integer> phaseOffsetTimeSetMap = new HashMap<>();
for (Map.Entry<Integer, String> entry : timeOffsetPhaseMap.entrySet()) {
Integer offsetTime = entry.getKey();
String phaseNo = entry.getValue();
if (timeOffsetPhaseMap.containsValue(phaseNo)) {
Integer currentOffsetTime = phaseOffsetTimeSetMap.get(phaseNo);
if (Math.abs(offsetTime) > Math.abs(currentOffsetTime)) {
phaseOffsetTimeSetMap.put(phaseNo, offsetTime);
} else {
phaseOffsetTimeSetMap.put(phaseNo, currentOffsetTime);
}
} else {
phaseOffsetTimeSetMap.put(phaseNo, offsetTime);
}
}
List<Integer> subTimeList = phaseOffsetTimeSetMap.entrySet().stream().map(entry -> entry.getValue()).collect(Collectors.toList()).stream().filter(i -> i < 0).collect(Collectors.toList());
int subTimeSum = subTimeList.stream().mapToInt(Integer::intValue).sum();
int lastAddTimeSum = 0;
for (Map.Entry<String, Integer> entry : phaseOffsetTimeSetMap.entrySet()) {
String phaseNo = entry.getKey();
Integer offsetTime = entry.getValue();
if (offsetTime > 0 && subTimeSum + offsetTime <= 0) {
// 当前相位可加时间就是最大可加时间,可加时间有可减时间
subTimeSum += offsetTime;
lastAddTimeSum += offsetTime;
phaseTimeOptResultMap.put(phaseNo, offsetTime);
}
if (offsetTime < 0) {
if (lastAddTimeSum <= Math.abs(offsetTime)) {
phaseTimeOptResultMap.put(phaseNo, lastAddTimeSum < 0 ? lastAddTimeSum : -lastAddTimeSum);
} else {
lastAddTimeSum += offsetTime;
phaseTimeOptResultMap.put(phaseNo, offsetTime);
}
}
}
return phaseTimeOptResultMap;
}
/**
* 计算路口各转向所需的通行时长(秒)
* 相位的通行时长需大于等于最小绿灯时长,并且小于等于最大绿灯时长
* 车头间距(米):车间据如果不在20和7之间取9(默认9)
* 车头时距(秒):车时距如果不在2和5之间取2.8(默认2.8)
* 排队车辆 = 排队长度 / 车头间距
* 通行时长 = 排队车辆 * 车头时距
* */
* 是否需要拆分相位灯组
* 同一相位内不同转向同时存在可加可减,需要拆分相位
*
* @param timeOffsetPhaseMap
* @return
*/
private boolean isSplitPhase(Map<Integer, String> timeOffsetPhaseMap) {
HashMap<String, Boolean> phaseOffsetMap = new HashMap<>();
for (Map.Entry<Integer, String> entry : timeOffsetPhaseMap.entrySet()) {
Boolean gtZore = entry.getKey() >= 0 ? Boolean.TRUE : Boolean.FALSE;
String phaseNo = entry.getValue();
if (phaseOffsetMap.containsKey(phaseNo) && !phaseOffsetMap.containsValue(gtZore)) {
return true;
}
}
return false;
}
/**
* 计算路口各相位调整时长(秒)
* 相位所需总放行时长 = sum(相位所需放行时长) - 总放行时长
* 相位所需总放行时长 = sum(相位所需放行时长) - 总放行时长(周期)
* 相位可减时长 = 相位有效绿灯时长 - 相位所需时长,同时“相位剩余有效绿灯时长”必须介于最小绿时长与最大绿时长之间
* 相位可加时长 = 相位所需时长 - 有效绿灯时长
* 相位可用总时长 = sum(相位可减时长)
* 相位优化时长 = 原相位时长 + 相位调整时长(相位可减时长或相位可加时长)
*
* 结果输出
* 路口编号、计划号、方案号、相位号、原相位时长、优化后相位时长、调整时长
* */
// 方案优化下发
* @param phaseMap
* @param crossTurnDataRealtimeDTOS
* @return key:相位可加减时间 value:相位编号
*/
private Map<Integer, String> getTimeOffsetPhaseMap(Map<String, CrossPhaseDTO> phaseMap, List<CrossTurnDataRealtimeDTO> crossTurnDataRealtimeDTOS) {
Map<Integer, String> timeOffsetPhaseMap = new HashMap<>();
crossTurnDataRealtimeDTOS.forEach(item -> {
String key = String.join(Constants.SystemParam.SEPARATOR_UNDER_LINE, String.valueOf(item.getInDir()), String.valueOf(item.getTurnType()));
Double passTime = item.getPassTime();
phaseMap.entrySet().forEach(entry -> {
String crossIdDirTurn = entry.getKey();
CrossPhaseDTO crossPhaseDTO = entry.getValue();
String phaseNo = crossPhaseDTO.getPhaseNo();
if (StringUtils.equalsIgnoreCase(key, crossIdDirTurn)) {
Integer realOptGreenTime = getRealOptGreenTime(passTime, crossPhaseDTO);
Integer greenTimeOffset = realOptGreenTime - crossPhaseDTO.getGreenTime() - crossPhaseDTO.getGreenFlashTime();
timeOffsetPhaseMap.put(greenTimeOffset, phaseNo);
}
});
});
return timeOffsetPhaseMap;
}
// 记录已优化的路口
CROSS_OPT.add(crossId);
private Integer getRealOptGreenTime(Double passTime, CrossPhaseDTO crossPhaseDTO) {
int passTimeInt = passTime.intValue();
if (passTimeInt <= crossPhaseDTO.getMinGreenTime()) {
passTimeInt = crossPhaseDTO.getMinGreenTime();
}
if (passTime >= crossPhaseDTO.getMaxGreenTime()) {
passTimeInt = crossPhaseDTO.getMaxGreenTime();
}
return passTimeInt;
}
private boolean isOffLineOrSpecialControlMode(String crossId) throws Exception {
JsonViewObject jsonViewObject = utcFeignClients.runningStatusAlarm();
if (Objects.isNull(jsonViewObject) || jsonViewObject.getCode() != 200) {
log.error("路口优化获取当前路口控制模式异常!");
throw new Exception();
}
List<SignalStatusVO> signalStatusVOS = (List<SignalStatusVO>) jsonViewObject.getContent();
List<SignalStatusVO> resultList = signalStatusVOS.stream().filter(signalStatusVO -> Objects.equals(crossId, signalStatusVO.getCrossId())).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(resultList)) {
SignalStatusVO signalStatusVO = resultList.get(0);
if (0 == signalStatusVO.getStatus() || 1 != signalStatusVO.getControlType()) {
return true;
}
}
return false;
}
/**
* 相位配时信息
* key:路口编号_方向类型_转向类型
* value:相位配时信息
*
* @return
*/
private Map<String, CrossPhaseDTO> listPhaseList() throws Exception {
......@@ -137,21 +268,21 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
Integer dir;
String[] turnArr;
Set<String> turnSet = new HashSet<>();
for(CrossSectionDTO section : sectionInfos) { // 运行时段列表
for (CrossSectionDTO section : sectionInfos) { // 运行时段列表
crossId = section.getCrossId();
CrossSchemeDTO scheme = schemeMap.get(section.getSchemeId()); // 方案信息
for(CrossPhaseDTO phase : scheme.getPhaseInfos()) { // 相位信息列表
for (CrossPhaseDTO phase : scheme.getPhaseInfos()) { // 相位信息列表
for(CrossLightsDTO lights : phase.getLightsInfos()) { // 灯组信息列表
for (CrossLightsDTO lights : phase.getLightsInfos()) { // 灯组信息列表
dir = lights.getDir();
for(LaneInfoDTO lane : lights.getLaneInfos()) {
for (LaneInfoDTO lane : lights.getLaneInfos()) {
// 车道转向转换为转向类型,并去重
turnArr = TurnConvertEnum.getCodeByKey(lane.getTurn()).split(Constants.SystemParam.SEPARATOR_UNDER_LINE);
turnSet.addAll(Arrays.stream(turnArr).collect(Collectors.toSet()));
}
for(String turn : turnSet) {
for (String turn : turnSet) {
key = crossId + Constants.SystemParam.SEPARATOR_UNDER_LINE + dir + Constants.SystemParam.SEPARATOR_UNDER_LINE + turn;
phase.setDirType(dir);
......@@ -167,6 +298,7 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
/**
* 获取当前运行时段信息
*
* @param schedulesDTOList 调度列表
* @return
*/
......@@ -175,24 +307,24 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
List<CrossSectionDTO> sectionInfos = new ArrayList<>();
long currentTime = DateUtil.dateToStamp(DateUtil.getTime(), net.wanji.common.framework.Constants.DATE_FORMAT.E_DATE_FORMAT_TIME.getStrFormat()); // 获取当前时间
int week = DateUtil.getWeek(new Date()) == Constants.SystemParam.ZERO ? WeekEnum.SUNDAY.getCode() : DateUtil.getWeek(new Date()); // 获取当前星期
for(CrossSchedulesDTO schedules : schedulesDTOList) {
for (CrossSchedulesDTO schedules : schedulesDTOList) {
if(schedules.getWeek() == Constants.SystemParam.ZERO &&
if (schedules.getWeek() == Constants.SystemParam.ZERO &&
!DateUtil.getDate().equals(schedules.getSpecialDate())) { // 先判断是否为特殊日期,如果特殊日期与当前日期不相同,则跳过
continue;
} else if(schedules.getWeek() != week) {
} else if (schedules.getWeek() != week) {
continue;
}
// 时段信息列表
for(CrossSectionDTO section : schedules.getSectionInfos()) {
for (CrossSectionDTO section : schedules.getSectionInfos()) {
// 过滤非当前时段数据
if(currentTime < DateUtil.dateToStamp(section.getStartTime()) || currentTime > DateUtil.dateToStamp(section.getEndTime())) {
if (currentTime < DateUtil.dateToStamp(section.getStartTime()) || currentTime > DateUtil.dateToStamp(section.getEndTime())) {
continue;
}
// 过滤控制模式为“非定周期”的数据
if(!ControlModeEnum.FIXED_PERIOD.equals(section.getControlMode())) {
if (!ControlModeEnum.FIXED_PERIOD.equals(section.getControlMode())) {
continue;
}
section.setPlanNo(schedules.getPlanNo());
......@@ -203,12 +335,11 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
}
/**
* 获取所有信控路口转向实时数据
* key 路口编号
* value 转向实时数据
*
* @return
*/
public Map<String, List<CrossTurnDataRealtimeDTO>> listTurnDataRealtime() {
......
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