Commit e58666f4 by chamberone

feat: 调整包名

1 parent f7a8e8b2
package com.dituhui.pea.dispatch;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.UncheckedIOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig;
import com.dituhui.pea.dispatch.pojo.Customer;
import com.dituhui.pea.dispatch.pojo.Depot;
import com.dituhui.pea.dispatch.pojo.Location;
import com.dituhui.pea.dispatch.pojo.Technician;
import com.dituhui.pea.dispatch.pojo.VehicleRoutingSolution;
/**
* 真实数据测试
*
* @author dell
*
*/
public class DispatherTest2 {
static long terminationSpentLimitSeconds = 1 * 60;
public static void main(String[] args) throws UncheckedIOException, FileNotFoundException {
System.out.println("hello");
// 创建解决方案对象
Map<Integer, String> customerIndexMap = loadCustomerIndex();
Map<Integer, String> technicianIndexMap = loadTechnicianIndex();
Map<String, Set<String>> technicianCodeSkillsMap = loadTechnicianCodeSkillsMap();
Map<String, String> customerCodeSkillMap = loadCustomerCodeSkillMap();
Map<String, Map<String, Long>> preferredlocationDistanceMap =loadPreferredlocationDistanceMap();
Map<String, Integer> customerCodeServiceTimeMap = loadCustomerCodeServiceTimeMap();
VehicleRoutingSolution problem = createVehicleRoutingSolution(customerIndexMap, technicianIndexMap,
technicianCodeSkillsMap, customerCodeSkillMap, preferredlocationDistanceMap,customerCodeServiceTimeMap);
// 创建求解器配置
// 创建 SolverConfig 对象,并设置求解器配置
SolverConfig solverConfig = new SolverConfig();
solverConfig.setSolutionClass(VehicleRoutingSolution.class);
solverConfig.withEntityClassList(Arrays.asList(Technician.class, Customer.class));// 这里不能漏掉,否则约束不生效
solverConfig.withTerminationSpentLimit(Duration.ofSeconds(terminationSpentLimitSeconds));
// 约束条件
solverConfig.withConstraintProviderClass(com.dituhui.pea.dispatch.constraint.VehicleRoutingConstraintProvider.class);
// 创建求解器
SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.create(solverConfig);
Solver<VehicleRoutingSolution> solver = solverFactory.buildSolver();
VehicleRoutingSolution solution = solver.solve(problem);
printSolution(solution, customerIndexMap, technicianIndexMap);
System.out.println("hardScore: " + solution.getScore().hardScore());
System.out.println("softScore: " + solution.getScore().softScore());
}
private static Map<String, Integer> loadCustomerCodeServiceTimeMap()
throws UncheckedIOException, FileNotFoundException {
List<String> customerServiceTime = IOUtils.readLines(new FileInputStream("data/customerServiceTime.csv"), "utf-8");
Map<String, Integer> customerCodeServiceTimeMap = new HashMap<String, Integer>();// code-time
for (int i = 0; i < customerServiceTime.size(); i++) {
String line = customerServiceTime.get(i);
String[] temps = line.split(",");
customerCodeServiceTimeMap.put(temps[0], Integer.parseInt(temps[1]));
}
return customerCodeServiceTimeMap;
}
private static Map<String, Map<String, Long>> loadPreferredlocationDistanceMap()
throws UncheckedIOException, FileNotFoundException {
List<String> technicianCodeLocation = IOUtils
.readLines(new FileInputStream("data/technicianLocation.csv"), "utf-8");
Map<String, String> technicianCodeLocationMap = new HashMap<String, String>();// 序号-code
for (int i = 0; i < technicianCodeLocation.size(); i++) {
String line = technicianCodeLocation.get(i);
String[] temps = line.split(",");
technicianCodeLocationMap.put(temps[0], temps[1] + "," + temps[2]);
}
List<String> customerCodeLocation = IOUtils
.readLines(new FileInputStream("data/customerLocation.csv"), "utf-8");
Map<String, String> customerCodeLocationMap = new HashMap<String, String>();// 序号-code
for (int i = 0; i < customerCodeLocation.size(); i++) {
String line = customerCodeLocation.get(i);
String[] temps = line.split(",");
customerCodeLocationMap.put(temps[0], temps[1] + "," + temps[2]);
}
// 生成订单和技术员的偏好距离Map 技术员-订单-距离
Map<String, Map<String, Long>> customerTecnicianDistanceMap = new HashMap<String, Map<String, Long>>();
customerCodeLocationMap.forEach((customerCode, value) -> {
technicianCodeLocationMap.forEach((technicianCode, value2) -> {
String[] temps = RegExUtils.removeAll(value, "\"").split(",");
String[] temps2 = RegExUtils.removeAll(value2, "\"").split(",");
long distance = (long) getDistance(Double.parseDouble(temps[1]), Double.parseDouble(temps[0]),
Double.parseDouble(temps2[1]), Double.parseDouble(temps2[0]));
Map<String, Long> customerMaps = customerTecnicianDistanceMap.get(technicianCode);
if(null == customerMaps) {
customerMaps = new HashMap<String, Long>();
customerTecnicianDistanceMap.put(technicianCode, customerMaps);
}
customerMaps.put(customerCode, distance);
});
});
return customerTecnicianDistanceMap;
}
/**
* 获取经纬度距离
*
* @param lat1 y
* @param lon1 x
* @param lat2 y
* @param lon2 x
* @return
*/
public static double getDistance(double lat1, double lon1, double lat2, double lon2) {
double diffLongitudes = Math.toRadians(Math.abs(lon1 - lon2));
double diffLatitudes = Math.toRadians(Math.abs(lat1 - lat2));
double slat = Math.toRadians(lat1);
double flat = Math.toRadians(lat2);
// haversine formula
double a = Math.sin(diffLatitudes / 2) * Math.sin(diffLatitudes / 2)
+ Math.cos(slat) * Math.cos(flat) * Math.sin(diffLongitudes / 2) * Math.sin(diffLongitudes / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // angular distance in radians
return 6378137 * c;
}
private static Map<String, String> loadCustomerCodeSkillMap() throws UncheckedIOException, FileNotFoundException {
List<String> customerSkill = IOUtils.readLines(new FileInputStream("data/customerSkill.csv"), "utf-8");
Map<String, String> customerCodeSkillMap = new HashMap<String, String>();// code-技能
for (int i = 0; i < customerSkill.size(); i++) {
String line = customerSkill.get(i);
String[] temps = line.split(",");
customerCodeSkillMap.put(temps[0], temps[1]);
}
return customerCodeSkillMap;
}
private static Map<String, Set<String>> loadTechnicianCodeSkillsMap()
throws UncheckedIOException, FileNotFoundException {
List<String> technicianSkills = IOUtils.readLines(new FileInputStream("data/technicianSkills.csv"), "utf-8");
Map<String, Set<String>> technicianCodeSkillsMap = new HashMap<String, Set<String>>();// code-技能
for (int i = 0; i < technicianSkills.size(); i++) {
String line = technicianSkills.get(i);
String[] temps = line.split(",");
String code = temps[0];
Set<String> skills = technicianCodeSkillsMap.get(code);
if (null == skills) {
skills = new HashSet<>();
technicianCodeSkillsMap.put(code, skills);
}
skills.add(temps[1]);
}
return technicianCodeSkillsMap;
}
private static Map<Integer, String> loadTechnicianIndex() throws UncheckedIOException, FileNotFoundException {
List<String> technicianIndexlines = IOUtils.readLines(new FileInputStream("data/technicianIndex.csv"), "utf-8");
Map<Integer, String> technicianIndexMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < technicianIndexlines.size(); i++) {
technicianIndexMap.put(i + 1, technicianIndexlines.get(i));
}
return technicianIndexMap;
}
private static Map<Integer, String> loadCustomerIndex() throws UncheckedIOException, FileNotFoundException {
List<String> customerIndexlines = IOUtils.readLines(new FileInputStream("data/customerIndex.csv"), "utf-8");
Map<Integer, String> customerIndexMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < customerIndexlines.size(); i++) {
customerIndexMap.put(i + 1, customerIndexlines.get(i));
}
return customerIndexMap;
}
private static VehicleRoutingSolution createVehicleRoutingSolution(Map<Integer, String> customerIndexMap,
Map<Integer, String> technicianIndexMap, Map<String, Set<String>> technicianCodeSkillsMap,
Map<String, String> customerCodeSkillMap, Map<String, Map<String, Long>> preferredlocationDistanceMap, Map<String, Integer> customerCodeServiceTimeMap) throws UncheckedIOException, FileNotFoundException {
VehicleRoutingSolution vehicleRoutingSolution = new VehicleRoutingSolution();
// 翻转map
Map<String, Integer> customerIndexMap2 = customerIndexMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
// 初始化距离矩阵
List<String> pathMatrixlines = IOUtils.readLines(new FileInputStream("data/pathMatrix.csv"), "utf-8");
long[][] pathMatrix = new long[customerIndexMap.keySet().size() + 1][customerIndexMap.keySet().size() + 1];
for (int i = 0; i < pathMatrixlines.size(); i++) {
String line = pathMatrixlines.get(i);
String[] temps = line.split(",");
for (int j = 0; j < temps.length; j++) {
pathMatrix[i][j] = (long) (Float.parseFloat(temps[j]) * 1000);
}
}
Map<Integer, Location> locationIndex = new HashMap<Integer, Location>();
for (int i = 0; i < pathMatrix.length; i++) {
// 1-6
locationIndex.put(i + 1, new Location(i + 1));
}
for (int i = 0; i < pathMatrix.length; i++) {
Location locationi = locationIndex.get(i + 1);
for (int j = 0; j < pathMatrix[i].length; j++) {
Location locationj = locationIndex.get(j + 1);
locationi.getDistanceMap().put(locationj, pathMatrix[i][j]);
}
}
// 初始化时间矩阵
// 初始化订单服务窗
List<String> customerWindowslines = IOUtils.readLines(new FileInputStream("data/customerWindows.csv"), "utf-8");
Map<Integer, Integer> customerStartMap = new HashMap<Integer, Integer>();
Map<Integer, Integer> customerEndMap = new HashMap<Integer, Integer>();
for (int i = 0; i < customerWindowslines.size(); i++) {
String line = customerWindowslines.get(i);
String[] temps = line.split(",");
customerStartMap.put(customerIndexMap2.get(temps[0]), 480 + Integer.parseInt(temps[1]));
customerEndMap.put(customerIndexMap2.get(temps[0]), 480 + Integer.parseInt(temps[2]));
}
// 初始化订单需要技能
Map<Integer, String> customerSkillMap = new HashMap<Integer, String>();
for (int i = 0; i < customerWindowslines.size(); i++) {
// 获取订单技能
customerSkillMap.put(i + 1, customerCodeSkillMap.get(customerIndexMap.get(i + 1)));
if (null == customerCodeSkillMap.get(customerIndexMap.get(i + 1))) {
System.err.printf("%s code:%s 没有技能 %n", i + 1, customerIndexMap.get(i + 1));
System.exit(0);
}
}
// 初始化订单+技能服务时间
List<Customer> customerList = new ArrayList<>();
for (int i = 0; i < customerIndexMap.keySet().size(); i++) {
customerList.add(new Customer(i + 1, customerIndexMap.get(i + 1), locationIndex.get(i + 2),
customerStartMap.get(i + 1), customerEndMap.get(i + 1), customerSkillMap.get(i + 1),
// 初始化技能服务时间
customerCodeServiceTimeMap.get(customerIndexMap.get(i + 1))));
}
// 初始化Depot
Depot depot = new Depot(1, locationIndex.get(1), 480, 1080);
// 初始化技术员
List<Technician> technicianList = new ArrayList<>();
for (int i = 0; i < technicianIndexMap.keySet().size(); i++) {
// 获取第i+1个技术员的技能set
Set<String> skills = technicianCodeSkillsMap.get(technicianIndexMap.get(i + 1));
if (null == skills || skills.size() == 0) {
System.err.printf("技术员%s code:%s 没有技能 %n", i + 1, technicianIndexMap.get(i + 1));
System.exit(0);
}
technicianList.add(new Technician(i + 1, technicianIndexMap.get(i + 1), depot, 480, 1080, skills,
preferredlocationDistanceMap.get(technicianIndexMap.get(i + 1))));
}
vehicleRoutingSolution.setCustomerList(customerList);
vehicleRoutingSolution.setDepot(depot);
vehicleRoutingSolution.setLocationList(new ArrayList<>(locationIndex.values()));
vehicleRoutingSolution.setTechnicianList(technicianList);
return vehicleRoutingSolution;
}
static void printSolution(VehicleRoutingSolution solution, Map<Integer, String> customerIndexMap,
Map<Integer, String> technicianIndexMap) {
System.out.println("技能约束:");
solution.getTechnicianList().forEach(technician -> {
System.out.printf("技术员%s(%s) %s%n", technician.getId(), technicianIndexMap.get((int) technician.getId()),
technician.getSkills());
for (Customer customer : technician.getCustomerList()) {
if (!technician.getSkills().contains(customer.getRequiredSkill())) {
// no match
System.err.printf(" 预约单%s(%s) %s%n", customer.getId(), customerIndexMap.get((int) customer.getId()),
customer.getRequiredSkill());
} else {
System.out.printf(" 预约单%s(%s) %s%n", customer.getId(), customerIndexMap.get((int) customer.getId()),
customer.getRequiredSkill());
}
}
});
AtomicInteger totalNum = new AtomicInteger(0);
solution.getTechnicianList().forEach(technician -> {
System.out.printf("技术员%s(%s) [%s,%s]%n", technician.getId(),
technicianIndexMap.get((int) technician.getId()), printTime(technician.getStartTime()),
printTime(technician.getEndTime()));
totalNum.addAndGet(technician.getCustomerList().size());
for (Customer customer : technician.getCustomerList()) {
Customer previousCustomer = customer.getPreviousCustomer();
int startPath, endPath;//路上时间
if (null == previousCustomer) {
startPath = technician.getDepot().getStartTime();
// endPath = startPath + customer.getLocation().getPathTimeTo(technician.getDepot().getLocation());
endPath = startPath + technician.getDepot().getLocation().getPathTimeTo(customer.getLocation());
} else {
startPath = previousCustomer.getDepartureTime();
// endPath = startPath + customer.getLocation().getPathTimeTo(previousCustomer.getLocation());
endPath = startPath + previousCustomer.getLocation().getPathTimeTo(customer.getLocation());
}
System.out.printf(" 预约单%s(%s) 预约时间窗[%s=>%s] 路上时间[%s=>%s] 早到等待时间[%s=>%s] 派工时间[%s=>%s] 迟到时间[%s=>%s]%n",
customer.getId(), customerIndexMap.get((int) customer.getId()),
// 预约时间窗
printTime(customer.getStartTime()), printTime(customer.getEndTime()),
// 路上时间
printTime(startPath), printTime(endPath),
// 早到等待时间
customer.getArrivalTime() < customer.getStartTime() ? printTime(endPath) : "",
customer.getArrivalTime() < customer.getStartTime() ? printTime(customer.getStartTime()) : "",
// 派工时间
printTime(customer.getArrivalTime()), printTime(customer.getDepartureTime()),
// 迟到时间
customer.getArrivalTime() > customer.getEndTime() ? printTime(customer.getEndTime()) : "",
customer.getArrivalTime() > customer.getEndTime() ? printTime(customer.getArrivalTime()) : "");
}
});
}
private static String printTime(int startTime) {
int hour = startTime / 60;
int minite = startTime % 60;
return StringUtils.leftPad("" + hour, 2, '0') + ":" + StringUtils.leftPad("" + minite, 2, '0');
}
}
......@@ -8,7 +8,7 @@ import org.optaplanner.core.api.score.stream.ConstraintProvider;
import com.dituhui.pea.dispatch.pojo.Customer;
import com.dituhui.pea.dispatch.pojo.Technician;
public class VehicleRoutingConstraintProvider implements ConstraintProvider {
public class DispatchConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory factory) {
......
......@@ -40,7 +40,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.dituhui.pea.dispatch.constraint.VehicleRoutingConstraintProvider;
import com.dituhui.pea.dispatch.constraint.DispatchConstraintProvider;
import com.dituhui.pea.dispatch.pojo.VehicleRoutingSolution;
import com.dituhui.pea.common.Result;
import com.dituhui.pea.dispatch.service.DispatchService;
......@@ -79,7 +79,7 @@ public class DispatchServiceImpl implements DispatchService {
solverConfig.withTerminationSpentLimit(Duration.ofSeconds(60));
// 约束条件
solverConfig.withConstraintProviderClass(VehicleRoutingConstraintProvider.class);
solverConfig.withConstraintProviderClass(DispatchConstraintProvider.class);
// 创建求解器
SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.create(solverConfig);
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!