springboot整合OptaPlanner+Drl(规则文件)

首先简单介绍一下,前面自己学了drools规则引擎,但是好像学习了解drools后,发现的optaplanner这个基于drools规则引擎开发的一个约束求解器,用于一些业务排程,机台排程,计划排程等问题以及类似的应用场景。说的更通俗易懂点,就是有几个变量,会根据实际情况进行变化,而这几个变量变化就会造成结果产生变化的情况。

例如:一个生产任务,可能受员工人数,原材料,生产设备的影响导致生产任务可能会有变化(例如都充足情况下,然后可能提前完成生产任务,如果原材料数量不足,生成到一半然后没材料了,导致生产任务停止)

这些都是optaplanner可以来帮我实现的一些业务场景。官网有详细介绍,需要的可以自行企业官网细细了解。

简单介绍完背景后,直接上实际操作。

第1步:创建一个springboot项目

第2步:导入optaplanner的相关依赖,(此处避坑,如果是通过drl文件实现的规则,则使用9.x之前的版本,如果是通过约束流来实现规则判断的则使用9.X以后得版本),因为9.0版本后不使用drl规则文件来实现了。

<!--OptaPlanner的核心库,提供了求解器、规划变量、评分计算等核心功能的实现.-->
<dependency>
  <groupId>org.optaplanner</groupId>
  <artifactId>optaplanner-core</artifactId>
  <version>8.20.0.Final</version>
</dependency>
<!-- OptaPlanner 在 Spring Boot 中集成的依赖.-->
<dependency>
    <groupId>org.optaplanner</groupId>
    <artifactId>optaplanner-spring-boot-starter</artifactId>
    <version>8.20.0.Final</version>
</dependency>

第3步:创建对应的配置文件src/main/resources/application.yaml

server:
  address: 127.0.0.1
  port: 8080
  servlet:
    context-path: /api
spring:
  config:
    name: optaplanner_springboot
 
 

第四步:创建drl规则文件src/main/resources/rules/taskAssignmentDrools.drl(drl规则文件就是大概意思就是当什么什么的时候,我们就进行软,硬规则的操作。比如这里面就是进行扣分)。

package rules;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;
import com.qiyan.smartrule.entity.Task;
import com.qiyan.smartrule.entity.Machine;
import com.qiyan.smartrule.entity.TaskAssignment;



global HardSoftScoreHolder scoreHolder;


rule "yarnTypeMatch"
when
    Task(machine != null, machine.yarnType != requiredYarnType)
then
    scoreHolder.addHardConstraintMatch(kcontext, -10000);
end

rule "machineCapacity"
when
    $machine : Machine($capacity : capacity)
    accumulate(
        Task(
            machine == $machine,
            $amount : amount);
        $amountTotal : sum($amount);
        $amountTotal > $capacity
        )
then
    scoreHolder.addHardConstraintMatch(kcontext, $capacity - $amountTotal);
end

rule "machineCost_used"
when
    $machine : Machine($cost : cost)
    exists Task(machine == $machine)
then
    scoreHolder.addSoftConstraintMatch(kcontext, -$cost);
end

第4步:创建optaplanner对应的xml文件src/main/resources/taskassignmentConfiguration.xml

其实这个xml文件也可以不要,也可以写一个对应的optaplanner_config配置类,实现动态设置对应的Solver对象。

<?xml version="1.0" encoding="UTF-8"?>
<solver>
    
    <!-- 解决方案对象 -->
    <solutionClass>com.qiyan.smartrule.entity.TaskAssignment</solutionClass>
    <!-- 因变量对象 -->
    <entityClass>com.qiyan.smartrule.entity.Task</entityClass>


    <!-- drl规则文件地址 -->
    <scoreDirectorFactory>
        <scoreDrl>rules/taskAssignmentDrools.drl</scoreDrl>
    </scoreDirectorFactory>

    <!-- 规则执行时间限制,超过时间会返回类似兜底数据 -->
    <termination>
        <secondsSpentLimit>10</secondsSpentLimit>
    </termination>
</solver>

第5步:创建对应的entity对象,可以理解为这些就是一个任务的变量。

5.1 AbstractPersistable 这个类就是用来对任务id进行排序等操作的,你可以通过compareTo方法自己实现自己的一些排序等操作。

package com.qiyan.smartrule.entity;
 
import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.optaplanner.core.api.domain.lookup.PlanningId;
 
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AbstractPersistable implements Serializable, Comparable<AbstractPersistable> {
    @PlanningId
    protected Long id;

    @Override
    public int compareTo(AbstractPersistable other) {
        return new CompareToBuilder().append(getClass().getName(), other.getClass().getName()).append(id, other.id)
                .toComparison();
    }

    @Override
    public String toString() {
        return getClass().getName().replaceAll(".*\\.", "") + "-" + id;
    }
}

5.2Machine (机台类)

package com.qiyan.smartrule.entity;


import lombok.Getter;

@Getter
public class Machine extends AbstractPersistable {
    private String yarnType;
    private int capacity;
    private int cost;

    public void setYarnType(String yarnType) {
        this.yarnType = yarnType;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }

    public void setCost(int cost) {
        this.cost = cost;
    }
    public Machine(long id, String yarnType, int capacity, int cost) {
        super(id);
        this.yarnType = yarnType;
        this.capacity = capacity;
        this.cost = cost;
    }
}

5.3Task (任务类)

package com.qiyan.smartrule.entity;
 
import lombok.Setter;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.PlanningVariable;

@Setter
@PlanningEntity
public class Task  extends AbstractPersistable {

    //当前任务的类型
    private String requiredYarnType;
    private int amount;

    private Machine machine;

    public String getRequiredYarnType() {
        return requiredYarnType;
    }

    public int getAmount() {
        return amount;
    }
    //规划变量是指可以被调整和优化的属性 (可以理解为变量)
    @PlanningVariable(valueRangeProviderRefs={"machineRange"})
    public Machine getMachine() {
        return machine;
    }

    public Task(){}

    public Task(long id, String requiredYarnType, int amount) {
        super(id);
        this.requiredYarnType = requiredYarnType;
        this.amount = amount;
    }
}

5.4 TaskAssignment (任务结果类)

package com.qiyan.smartrule.entity;

import java.util.List;

import lombok.*;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;


@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@PlanningSolution
public class TaskAssignment extends AbstractPersistable {
    //这个就是最终的软硬分对象
    @PlanningScore
    private HardSoftScore score;
    //问题的事实集合 (可以理解为因变量)
    @ProblemFactCollectionProperty
    @ValueRangeProvider(id = "machineRange")
    private List<Machine> machineList;
    //问题的规划实体集合(可以理解为变量)
    @PlanningEntityCollectionProperty
    @ValueRangeProvider(id = "taskRange")
    private List<Task> taskList;


    public TaskAssignment(List<Machine> machineList, List<Task> taskList) {
        //super(0);
        this.machineList = machineList;
        this.taskList = taskList;
    }

}

第6步:创建对应的接口测试

 @Test
    void optaplannerTest() {
        startPlan();
    }
    private static void startPlan(){
        List<Machine> machines = getMachines();
        List<Task> tasks = getTasks();

//        InputStream ins = App.class.getResourceAsStream("");

        SolverFactory<TaskAssignment> solverFactory = SolverFactory.createFromXmlResource("taskassignmentConfiguration.xml");
        Solver<TaskAssignment> solver = solverFactory.buildSolver();
        TaskAssignment unassignment = new TaskAssignment(machines, tasks);
        TaskAssignment assigned = solver.solve(unassignment);//启动引擎
        List<Machine> machinesAssigned = assigned.getTaskList().stream().map(Task::getMachine).distinct().collect(Collectors.toList());
        for(Machine machine : machinesAssigned) {
            System.out.print("\n" + machine + ":");
            List<Task> tasksInMachine = assigned.getTaskList().stream().filter(x -> x.getMachine().equals(machine)).collect(Collectors.toList());
            for(Task task : tasksInMachine) {
                System.out.print("->" + task);
            }
        }
    }


    private static List<Machine> getMachines() {
        // 六个机台
        Machine m1 = new Machine(1, "Type_A", 300, 100);
        Machine m2 = new Machine(2, "Type_A", 1000, 100);
        Machine m3 = new Machine(3, "TYPE_B", 1000, 300);
        Machine m4 = new Machine(4, "TYPE_B", 1000, 100);
        Machine m5 = new Machine(5, "Type_C", 1100, 100);
        Machine m6 = new Machine(6, "Type_D", 900, 100);

        List<Machine> machines = new ArrayList<Machine>();
        machines.add(m1);
        machines.add(m2);
        machines.add(m3);
        machines.add(m4);
        machines.add(m5);
        machines.add(m6);

        return machines;
    }

第7步;代码运行结果:

在本地搭建过程中遇到了几个问题:

问题一:大概意思就是找不到

import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;这个类,排错后发现这问题产生的原因是没有读取到drl文件,也就是xml配置中的XXXXX.drl文件不对,我之前没加rules造成的。

java.lang.IllegalArgumentException: The kieBase with kiePackages ([]) has no global field called scoreHolder.
Check if the rule files are found and if the global field is spelled correctly.

	at org.optaplanner.constraint.drl.DrlScoreDirectorFactory.assertGlobalScoreHolderExists(DrlScoreDirectorFactory.java:82)
	at org.optaplanner.constraint.drl.DrlScoreDirectorFactory.<init>(DrlScoreDirectorFactory.java:60)
	at org.optaplanner.constraint.drl.DrlScoreDirectorFactoryService.createScoreDirectorFactory(DrlScoreDirectorFactoryService.java:53)
	at org.optaplanner.constraint.drl.AbstractDrlScoreDirectorFactoryService.buildScoreDirectorFactory(AbstractDrlScoreDirectorFactoryService.java:68)
	at org.optaplanner.constraint.drl.DrlScoreDirectorFactoryService.lambda$buildScoreDirectorFactory$0(DrlScoreDirectorFactoryService.java:47)
	at org.optaplanner.core.impl.score.director.ScoreDirectorFactoryFactory.decideMultipleScoreDirectorFactories(ScoreDirectorFactoryFactory.java:137)
	at org.optaplanner.core.impl.score.director.ScoreDirectorFactoryFactory.buildScoreDirectorFactory(ScoreDirectorFactoryFactory.java:55)
	at org.optaplanner.core.impl.solver.DefaultSolverFactory.buildScoreDirectorFactory(DefaultSolverFactory.java:177)
	at org.optaplanner.core.impl.solver.DefaultSolverFactory.<init>(DefaultSolverFactory.java:87)
	at org.optaplanner.core.api.solver.SolverFactory.createFromXmlResource(SolverFactory.java:56)
	at com.qiyan.smartrule.SmartruleApplicationTests.startPlan(SmartruleApplicationTests.java:56)
	at com.qiyan.smartrule.SmartruleApplicationTests.droolsTest(SmartruleApplicationTests.java:46)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

问题二:就是我创建测试类的时候没传对应的问题id过去,导致找不到对应的问题id。

java.lang.IllegalArgumentException: The planningId (null) of the member (field protected java.lang.Long com.qiyan.smartrule.entity.AbstractPersistable.id) of the class (class com.qiyan.smartrule.entity.Task) on externalObject (Task-null) must not be null.
Maybe initialize the planningId of the class (Task) instance (Task-null) before solving.
Maybe remove the @PlanningId annotation or change the @PlanningSolution annotation's LookUpStrategyType.

	at org.optaplanner.core.impl.domain.lookup.PlanningIdLookUpStrategy.extractPlanningId(PlanningIdLookUpStrategy.java:98)
	at org.optaplanner.core.impl.domain.lookup.PlanningIdLookUpStrategy.addWorkingObject(PlanningIdLookUpStrategy.java:38)
	at org.optaplanner.core.impl.domain.lookup.LookUpManager.addWorkingObject(LookUpManager.java:46)
	at org.optaplanner.core.impl.score.director.AbstractScoreDirector.lambda$setWorkingSolution$0(AbstractScoreDirector.java:182)
	at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.visitAllFacts(SolutionDescriptor.java:912)
	at org.optaplanner.core.impl.score.director.AbstractScoreDirector.setWorkingSolution(AbstractScoreDirector.java:181)
	at org.optaplanner.constraint.drl.DrlScoreDirector.setWorkingSolution(DrlScoreDirector.java:68)
	at org.optaplanner.core.impl.solver.scope.SolverScope.setWorkingSolutionFromBestSolution(SolverScope.java:258)
	at org.optaplanner.core.impl.solver.AbstractSolver.solvingStarted(AbstractSolver.java:80)
	at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:240)
	at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:208)
	at com.qiyan.smartrule.SmartruleApplicationTests.startPlan(SmartruleApplicationTests.java:59)
	at com.qiyan.smartrule.SmartruleApplicationTests.droolsTest(SmartruleApplicationTests.java:46)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

  • 36
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值