近期一个客户提出一个想法,他们想要将某一个车间,由原本的手动人工排产,升级为系统自动排产。一听这个话题,这不就是APS么,客户又提出能不能根据现有的条件,先简单的给一个demo,看看算法实现的效果。
原本是想自己亲自动手,后来一想,是不是DeepSeek可以帮我搞定呢,先上结果(部分是作者君自行调整后的内容)。
代码切片
// 产品 工序
@Data
public class Process {
private final String name;
public Process(String name) { this.name = name; }
}
@Data
public class Product {
private String name;
public Product(String name) {
this.name = name;
}
}
// 工艺路线
public class ProcessRoute {
private String product;
private LinkedList<Process> processes = new LinkedList<>();
public ProcessRoute(Product product, LinkedList<Process> processes) {
this.product = product.getName();
this.processes = processes;
}
public ProcessRoute(String product, LinkedList<Process> processes) {
this.product = product;
this.processes = processes;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public LinkedList<Process> getProcesses() {
return processes;
}
public void setProcesses(LinkedList<Process> processes) {
this.processes = processes;
}
public Process findPre(String process){
Process pre = null;
Process now = null;
LinkedList<Process> processes = new LinkedList<>(this.processes);
while (true) {
now = processes.poll();
if (null == now) {
return null;
}
if (now.getName().equals(process)) {
return pre;
}else {
pre = now;
}
}
}
public Process findNxt(String process) {
Process nxt = null;
Process now = null;
LinkedList<Process> processes = new LinkedList<>(this.processes);
while (true) {
now = processes.poll();
if (null == now) {
return null;
}
if (now.getName().equals(process)) {
return processes.poll();
}
}
}
}
// 工序产能
public class ProcessCapacity {
private String process;
private Map<String, Integer> productCapacity;
public ProcessCapacity(Process process) {
this.process = process.getName();
this.productCapacity = new HashMap<>();
}
public ProcessCapacity(String process) {
this.process = process;
this.productCapacity = new HashMap<>();
}
public String getProcess() {
return process;
}
public void setProcess(String process) {
this.process = process;
}
public Map<String, Integer> getProductCapacity() {
return productCapacity;
}
public void setProductCapacity(Map<String, Integer> productCapacity) {
this.productCapacity = productCapacity;
}
public ProcessCapacity addProductCapacity(Product product, Integer capacity){
this.productCapacity.put(product.getName(), capacity);
return this;
}
}
// 主计划
public class MainOrder {
private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("yyyyMMdd");
private static final Random random = ThreadLocalRandom.current();
private static int i = 1;
private String orderNo;
private String product;
private int count;
private int finishCount;
private LocalDate lastDay;
public MainOrder(LocalDate date, String product, int count) {
this.orderNo = generateOrderId(date);
this.product = product;
this.count = count;
this.lastDay = date.plusDays(random.nextInt(50) + 15);
}
// 生成订单ID(符合DDMMYYYY-XXXX格式)
private String generateOrderId(LocalDate date) {
i = i + 1;
return DATE_FORMATTER.format(date) +
"-" + String.format("%04d", i);
}
private ProcessRoute processRoute;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public ProcessRoute getProcessRoute() {
return processRoute;
}
public void setProcessRoute(ProcessRoute processRoute) {
this.processRoute = processRoute;
}
public int getFinishCount() {
return finishCount;
}
public void setFinishCount(int finishCount) {
this.finishCount = finishCount;
}
public LocalDate getLastDay() {
return lastDay;
}
public void setLastDay(LocalDate lastDay) {
this.lastDay = lastDay;
}
@Override
public String toString() {
return "MainOrder{" +
"orderNo='" + orderNo + '\'' +
", product='" + product + '\'' +
", count=" + count +
", finishCount=" + finishCount +
", lastDay=" + lastDay +
", processRoute=" + processRoute +
'}';
}
public void addFinish(int count) {
this.finishCount = this.finishCount + count;
}
}
// 工序生产任务
@Data
@Accessors(chain = true)
public class ProcessTask {
private static int i = 0;
private String mainOrderNo;
private String taskNo;
private String product;
private String process;
private int taskCount;
private int finishCount = 0;
private boolean finish;
private LocalDate lastDate;
public boolean isFinish() {
return finishCount >= taskCount;
}
public ProcessTask initByMainOrder(MainOrder mainOrder) {
return this.setMainOrderNo(mainOrder.getOrderNo())
.setProduct(mainOrder.getProduct())
.setProcess(processRouteMap.get(mainOrder.getProduct()).getProcesses().peek().getName())
.setLastDate(mainOrder.getLastDay())
.setTaskCount(mainOrder.getCount())
.initTaskNo();
}
public ProcessTask initTaskNo() {
i++;
this.taskNo = this.mainOrderNo + "-" + i;
return this;
}
public int getRemainCount(){
return taskCount - finishCount;
}
public boolean needPlan(){
return taskCount>finishCount;
}
public void addFinish(int count){
this.finishCount = this.finishCount + count;
}
}
// 工序日计划
@Data
@Accessors(chain = true)
public class ProcessPlan {
private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("yyyyMMdd");
private static final Random random = ThreadLocalRandom.current();
private static int i = 1;
private String mainOrderNo;
private String taskNo;
private String planNo;
private String product;
private String process;
private int planCount;
private LocalDate planDate;
private LocalDate lastDate;
// 生成订单ID(符合DDMMYYYY-XXXX格式)
private String generateOrderId(LocalDate date) {
i = i + 1;
return DATE_FORMATTER.format(date) +
"-" + String.format("%04d", i);
}
public ProcessPlan initByTask(ProcessTask task, LocalDate date, int planCount) {
return this.setProcess(task.getProcess())
.setTaskNo(task.getTaskNo())
.setMainOrderNo(task.getMainOrderNo())
.setProduct(task.getProduct())
.setLastDate(task.getLastDate())
.setPlanDate(date)
.setPlanCount(planCount)
.setPlanNo(generateOrderId(date));
}
}
模拟执行
// demo模拟内容
/**
* Demo可以进一步扩展的内容
* 1、目前是工序产能,没有机台的产能,可以进一步拆解为机器产能,再合并到工序产能
* 2、目前按照计划100%完成,没有考虑计划可能未完成的情况,这部分可以与实际报工数据结合
* 3、每日计划没有按照产品进行计划合并,导致存在换线的情况(实操中可以不换线),这里可以优化
*/
public class Main {
private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("yyyy年MM月dd日");
public static Map<String, Product> productMap = new HashMap<>();
public static List<Product> productList = new ArrayList<>();
public static Map<String, Process> processMap = new HashMap<>();
public static Map<String, ProcessRoute> processRouteMap = new HashMap<>();
public static Map<String, ProcessCapacity> processCapacityMap = new HashMap<>();
public static List<MainOrder> mainOrderList = new ArrayList<>();
public static List<ProcessTask> processTaskList = new ArrayList<>();
public static List<ProcessPlan> processPlanList = new ArrayList<>();
public static Map<LocalDate, List<ProcessPlan>> daliyProcessPlanMap = new HashMap<>();
public static Map<String, List<ProcessPlan>> processPlanMap = new HashMap<>();
public static Map<String, List<ProcessTask>> processTaskMap = new HashMap<>();
public static Random random = ThreadLocalRandom.current();
static {
// 准备 产品数据、工序、产品工艺、工序产能
// 产品
Product product1 = new Product("电线1");
Product product2 = new Product("电线2");
Product product3 = new Product("电线3");
productMap.put(product1.getName(), product1);
productMap.put(product2.getName(), product2);
productMap.put(product3.getName(), product3);
productList.addAll(productMap.values());
// 工序
Process process1 = new Process("1束丝");
Process process2 = new Process("2绝缘挤出");
Process process3 = new Process("3成缆");
Process process4 = new Process("4护套挤出");
Process process5 = new Process("5产检");
processMap.put(process1.getName(), process1);
processMap.put(process2.getName(), process2);
processMap.put(process3.getName(), process3);
processMap.put(process4.getName(), process4);
processMap.put(process5.getName(), process5);
// 产品工艺
LinkedList<Process> processes = new LinkedList<>();
processes.offer(process1);
processes.offer(process2);
processes.offer(process3);
processes.offer(process4);
processes.offer(process5);
ProcessRoute processRoute1 = new ProcessRoute(product1, processes);
ProcessRoute processRoute2 = new ProcessRoute(product2, processes);
ProcessRoute processRoute3 = new ProcessRoute(product3, processes);
processRouteMap.put(processRoute1.getProduct(), processRoute1);
processRouteMap.put(processRoute2.getProduct(), processRoute2);
processRouteMap.put(processRoute3.getProduct(), processRoute3);
// 工序产能
ProcessCapacity processCapacity1 = new ProcessCapacity(process1)
.addProductCapacity(product1, 12000)
.addProductCapacity(product2, 9000)
.addProductCapacity(product3, 10000);
ProcessCapacity processCapacity2 = new ProcessCapacity(process2)
.addProductCapacity(product1, 9000)
.addProductCapacity(product2, 10000)
.addProductCapacity(product3, 12000);
ProcessCapacity processCapacity3 = new ProcessCapacity(process3)
.addProductCapacity(product1, 10000)
.addProductCapacity(product2, 12000)
.addProductCapacity(product3, 9000);
ProcessCapacity processCapacity4 = new ProcessCapacity(process4)
.addProductCapacity(product1, 12000)
.addProductCapacity(product2, 10000)
.addProductCapacity(product3, 9000);
ProcessCapacity processCapacity5 = new ProcessCapacity(process5)
.addProductCapacity(product1, 10000)
.addProductCapacity(product2, 10000)
.addProductCapacity(product3, 10000);
processCapacityMap.put(processCapacity1.getProcess(), processCapacity1);
processCapacityMap.put(processCapacity2.getProcess(), processCapacity2);
processCapacityMap.put(processCapacity3.getProcess(), processCapacity3);
processCapacityMap.put(processCapacity4.getProcess(), processCapacity4);
processCapacityMap.put(processCapacity5.getProcess(), processCapacity5);
}
public static void generateMainOrder(LocalDate date){
// 随机追加几个
int size = random.nextInt(3) + 1;
if (mainOrderList.isEmpty()){
size = random.nextInt(5) + 7;
}
System.out.println("每日新增主订单数据: \t , 日期: " + DATE_FORMATTER.format(date));
System.out.println("主计划单号 \t 产品 \t 计划生产数量 \t 完成数量 \t 交期 \t ");
for (int i = 0; i < size; i++) {
int count = random.nextInt(3000) + 3000;
int sub = random.nextInt(productList.size());
String product = productList.get(sub).getName();
MainOrder mainOrder = new MainOrder(date, product, count);
System.out.printf("%s \t %s \t %d \t %d \t %s \t ",
mainOrder.getOrderNo(), mainOrder.getProduct(), mainOrder.getCount(),
mainOrder.getFinishCount(), DATE_FORMATTER.format(mainOrder.getLastDay()));
System.out.println();
mainOrderList.add(mainOrder);
// 将MainOrder 分解到 第一个工序任务池
ProcessTask processTask = new ProcessTask().initByMainOrder(mainOrder);
processTaskList.add(processTask);
List<ProcessTask> orDefault = processTaskMap.getOrDefault(processTask.getProcess(), new ArrayList<>());
orDefault.add(processTask);
processTaskMap.put(processTask.getProcess(), orDefault);
}
System.out.println();
}
public static void generateProcessPlan(LocalDate date){
processMap.forEach((key, process) -> {
String name = process.getName();
// 从对应的工序任务池中获取任务
List<ProcessTask> processTaskPool = processTaskMap.getOrDefault(name, new ArrayList<>());
List<ProcessTask> collect = processTaskPool.stream().filter(ProcessTask::needPlan).sorted(Comparator.comparing(ProcessTask::getLastDate)).collect(Collectors.toList());
Map<String, Integer> productCapacity = processCapacityMap.get(name).getProductCapacity();
BigDecimal nowProcessCapacity = BigDecimal.ZERO;
BigDecimal fullProcessCapacity = BigDecimal.ONE;
for (ProcessTask processTask : collect) {
String product = processTask.getProduct();
BigDecimal oneOfCapacity = BigDecimal.ONE.divide(new BigDecimal(productCapacity.get(product)), 18, RoundingMode.DOWN);
BigDecimal remainCapacity = fullProcessCapacity.subtract(nowProcessCapacity);
BigDecimal nowCount = remainCapacity.multiply(new BigDecimal(productCapacity.get(product))).setScale(0, RoundingMode.DOWN);
if (nowCount.intValue() == 0) {
break;
}
if (processTask.getRemainCount() > nowCount.intValue()) {
genTaskToPlan(processTask, date, nowCount.intValue(), name);
break;
} else {
genTaskToPlan(processTask, date, processTask.getRemainCount(), name);
nowProcessCapacity = nowProcessCapacity.add(oneOfCapacity.multiply(new BigDecimal(processTask.getRemainCount())));
}
}
});
}
private static void genTaskToPlan(ProcessTask processTask, LocalDate date, int planCount, String name) {
ProcessPlan plan = new ProcessPlan().initByTask(processTask, date, planCount);
processPlanList.add(plan);
List<ProcessPlan> orDefault = processPlanMap.getOrDefault(name, new ArrayList<>());
orDefault.add(plan);
processPlanMap.put(name, orDefault);
List<ProcessPlan> orDefault1 = daliyProcessPlanMap.getOrDefault(date, new ArrayList<>());
orDefault1.add(plan);
daliyProcessPlanMap.put(date, orDefault1);
}
public static void main(String[] args) {
LocalDate date = LocalDate.now();
LocalDate lastDate = date.plusDays(15);
// 模拟每日重复
while (date.isBefore(lastDate)){
// 准备 主订单 随机生成
generateMainOrder(date);
// 依次按照产能对工序进行计划排产
generateProcessPlan(date);
// 输出每日计划内容
List<ProcessPlan> processPlans = daliyProcessPlanMap.getOrDefault(date, new ArrayList<>());
processPlans.sort((o1, o2) -> {
int compareTo = o1.getProcess().compareTo(o2.getProcess());
if (compareTo == 0) {
return o1.getProduct().compareTo(o2.getProduct());
}
return compareTo;
});
System.out.println("每日工序生产计划: \t , 日期: " + DATE_FORMATTER.format(date));
System.out.println("工序 \t 计划单号 \t 产品 \t 计划生产数量 \t 主计划单号 \t 工序任务号 \t");
processPlans.forEach(item ->{
System.out.printf("%s \t %s \t %s \t %d \t %s \t %s \t",
item.getProcess(), item.getPlanNo(), item.getProduct(),
item.getPlanCount(), item.getMainOrderNo(), item.getTaskNo()
);
System.out.println();
});
System.out.println();
// 按照完全完成的状态,推进到下一个工序
processPlans.forEach(item ->{
ProcessTask processTask = processTaskList.stream().filter(p -> p.getTaskNo().equals(item.getTaskNo())).findFirst().get();
processTask.addFinish(item.getPlanCount());
ProcessRoute processRoute = processRouteMap.get(item.getProduct());
Process nxtProcess = processRoute.findNxt(item.getProcess());
if (null == nxtProcess) {
// 追加到MainOrder完成
MainOrder mainOrder = mainOrderList.stream().filter(p -> p.getOrderNo().equals(item.getMainOrderNo())).findFirst().get();
mainOrder.addFinish(item.getPlanCount());
} else {
ProcessTask task = new ProcessTask()
.setTaskCount(item.getPlanCount())
.setMainOrderNo(item.getMainOrderNo())
.setProduct(item.getProduct())
.setLastDate(item.getLastDate())
.setProcess(nxtProcess.getName())
.initTaskNo();
processTaskList.add(task);
List<ProcessTask> orDefault = processTaskMap.getOrDefault(nxtProcess.getName(), new ArrayList<>());
orDefault.add(task);
processTaskMap.put(nxtProcess.getName(), orDefault);
}
});
// 输出每日结果 - 工序任务池情况
processTaskList.sort((o1, o2) -> {
int compareTo = o1.getProcess().compareTo(o2.getProcess());
if (compareTo == 0) {
return o1.getLastDate().compareTo(o2.getLastDate());
}
return compareTo;
});
System.out.println("每日工序任务池情况: \t , 日期: " + DATE_FORMATTER.format(date));
System.out.println("工序 \t 任务单号 \t 主计划单号 \t 产品 \t 计划生产数量 \t 完成数量 \t 交期 \t ");
processTaskList.forEach(item ->{
System.out.printf("%s \t %s \t %s \t %s \t %d \t %d \t %s \t ",
item.getProcess(),
item.getTaskNo(),item.getMainOrderNo(), item.getProduct(), item.getTaskCount(),
item.getFinishCount(), DATE_FORMATTER.format(item.getLastDate()));
System.out.println();
});
System.out.println();
// 输出每日结果 - 主计划
mainOrderList.sort(Comparator.comparing(MainOrder::getLastDay));
System.out.println("每日生产主计划完成情况: \t , 日期: " + DATE_FORMATTER.format(date));
System.out.println("主计划单号 \t 产品 \t 计划生产数量 \t 完成数量 \t 交期 \t ");
mainOrderList.forEach(item ->{
System.out.printf("%s \t %s \t %d \t %d \t %s \t ",
item.getOrderNo(), item.getProduct(), item.getCount(),
item.getFinishCount(), DATE_FORMATTER.format(item.getLastDay()));
System.out.println();
});
System.out.println();
date = date.plusDays(1);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
执行结果
// 程序运行结果:
每日新增主订单数据: , 日期: 2025年03月05日
主计划单号 产品 计划生产数量 完成数量 交期
20250305-0002 电线3 5154 0 2025年03月22日
20250305-0003 电线3 3967 0 2025年04月22日
20250305-0004 电线1 4837 0 2025年03月20日
20250305-0005 电线2 4842 0 2025年04月07日
20250305-0006 电线2 4548 0 2025年04月06日
20250305-0007 电线3 5579 0 2025年03月30日
20250305-0008 电线1 5618 0 2025年03月24日
每日工序生产计划: , 日期: 2025年03月05日
工序 计划单号 产品 计划生产数量 主计划单号 工序任务号
1束丝 20250305-0002 电线1 4837 20250305-0004 20250305-0004-3
1束丝 20250305-0004 电线1 978 20250305-0008 20250305-0008-7
1束丝 20250305-0003 电线3 5154 20250305-0002 20250305-0002-1
每日工序任务池情况: , 日期: 2025年03月05日
工序 任务单号 主计划单号 产品 计划生产数量 完成数量 交期
1束丝 20250305-0004-3 20250305-0004 电线1 4837 4837 2025年03月20日
1束丝 20250305-0002-1 20250305-0002 电线3 5154 5154 2025年03月22日
1束丝 20250305-0008-7 20250305-0008 电线1 5618 978 2025年03月24日
1束丝 20250305-0007-6 20250305-0007 电线3 5579 0 2025年03月30日
1束丝 20250305-0006-5 20250305-0006 电线2 4548 0 2025年04月06日
1束丝 20250305-0005-4 20250305-0005 电线2 4842 0 2025年04月07日
1束丝 20250305-0003-2 20250305-0003 电线3 3967 0 2025年04月22日
2绝缘挤出 20250305-0004-8 20250305-0004 电线1 4837 0 2025年03月20日
2绝缘挤出 20250305-0002-10 20250305-0002 电线3 5154 0 2025年03月22日
2绝缘挤出 20250305-0008-9 20250305-0008 电线1 978 0 2025年03月24日
每日生产主计划完成情况: , 日期: 2025年03月05日
主计划单号 产品 计划生产数量 完成数量 交期
20250305-0004 电线1 4837 0 2025年03月20日
20250305-0002 电线3 5154 0 2025年03月22日
20250305-0008 电线1 5618 0 2025年03月24日
20250305-0007 电线3 5579 0 2025年03月30日
20250305-0006 电线2 4548 0 2025年04月06日
20250305-0005 电线2 4842 0 2025年04月07日
20250305-0003 电线3 3967 0 2025年04月22日
与DeepSeek交流过程
具体与DeepSeek交流过程部分截图附在下方:
这一次,虽然没有给出完整的样例,但是DeepSeek给出了相对完整的思路,只是在深入到很细节的业务场景时,目前以AI的能力还是存在局限。虽都在说,AI来了,程序员的也要大结局了,但是根据实际情况来看,AI来了,可能应该是程序员的春天来了才对!
如果你也喜欢这篇文章,给我们点个赞吧~