Связанные переменные / якорная переменная и ошибка: только объединенная переменная поддерживает одноэлементное обратное - PullRequest
0 голосов
/ 22 января 2020

Предисловие: Поиск документов в Google + SO + не дал соответствующей информации.

Модель домена:

Моя модель домена пытается изобразить ProductionPlan, содержащий список машин. У каждой машины есть список связанных работ, то есть работа, у которой есть метод getNextEntry (): Job Job, создающий список работ.

Я пытался решить эту проблему с помощью цепочки PlanningVariables, но, видимо, не понимаю концепции chainedVariables / shadowVariables / anchorVariables. Насколько я понимаю, все задания объединяются в цепочку, и anchorShadowVariable указывает на начало списка, то есть на компьютер.

Для реализации связывания Job и Machine необходимо реализовать интерфейс или расширить суперкласс, поэтому я создал ChainSuperClass , Кроме того, я не знаю, нужно ли мне переписывать метод получения / установки для установки аннотаций в Machine-классе, я предположил, что, поскольку машина расширяет класс ChainSuperClass, эти аннотации переносятся.

Редактировать: Kotlin конкретизировать c также приветствуются улучшения.

Полный журнал ошибок выполнения моего кода теперь:

Exception in thread "main" java.lang.IllegalArgumentException: The entityClass (class optaplanner.productionPlan.domain.ChainSuperClass) has a InverseRelationShadowVariable annotated property (nextEntry) which does not return a Collection with sourceVariableName (machine) which is not chained. Only a chained variable supports a singleton inverse.

ChainSuperClass:

@PlanningEntity
abstract class ChainSuperClass {

@PlanningId
open val id = Random.nextInt().toString()

@InverseRelationShadowVariable(sourceVariableName = "machine")
abstract fun getNextEntry(): Job?

abstract fun setNextEntry(job: Job)
}

Задание:

@PlanningEntity
class Job(
    val jobType: JobType,
    val itemNumber: String,
    val orderNumber: String,
    val setupTime: Int,
    val productionTime: Int
) : ChainSuperClass() {

     @AnchorShadowVariable(sourceVariableName = "machine")
     var machine: Machine? = null

     private var nextEntry: Job? = null

     @PlanningVariable(
          valueRangeProviderRefs = ["jobList"],
          graphType = PlanningVariableGraphType.CHAINED
     )
     override fun getNextEntry(): Job? {
          return nextEntry
     }

     override fun setNextEntry(job: Job) {
          this.nextEntry = nextEntry
     }
}

Машина:

class Machine(override val id: String, val jobTypes: List<JobType>) : ChainSuperClass() {

    private var nextEntry: Job? = null

    override fun setNextEntry(job: Job) {
        this.nextEntry = job
    }

    override fun getNextEntry(): Job? {
        return nextEntry!!
    }
}

1 Ответ

2 голосов
/ 22 января 2020

Я думаю, что наиболее важная вещь, которую нужно реализовать с помощью связанных переменных, это: если у вас есть сущность, скажем Job A , и решатель присваивает значение (задание / машина) своей переменной, это не похоже на Цепочка строится вперед, начиная с Задание A . Это наоборот. Присваивая значение переменной планирования Job A , Job A подключается в конце существующей цепочки.

Пожалуйста, посмотрите на документация для получения более подробной информации о цепочках и примерах допустимых цепочек.

Поняв это, должно быть ясно, что имя переменной планирования Job должно быть чем-то вроде previousJobOrMachine (вы Возможно, понадобится что-то более простое, например previousStep), тогда как свойство nextJob является теневой переменной обратного отношения, полученной из этого (поэтому, когда Job X подключается к существующей цепочке, заканчивающейся Job C, присваивая Задание X .previousStep = Задание C, обратное отношение устанавливается автоматически: Задание C .nextJob = Задание X ).

На основании этой информации ваша модель должна выглядеть примерно так:

@PlanningEntity
abstract class ChainSuperClass {

@PlanningId
open val id = Random.nextInt().toString()

// changed sourceVariableName to point to the planning variable
@InverseRelationShadowVariable(sourceVariableName = "previousStep")
abstract fun getNextEntry(): Job?

abstract fun setNextEntry(job: Job)
}
@PlanningEntity
class Job(
    val jobType: JobType,
    val itemNumber: String,
    val orderNumber: String,
    val setupTime: Int,
    val productionTime: Int
) : ChainSuperClass() {

     // changed sourceVariableName to point to the planning variable
     @AnchorShadowVariable(sourceVariableName = "previousStep")
     var machine: Machine? = null

     // added planning variable
     private var previousStep: ChainSuperClass? = null
     private var nextEntry: Job? = null

     @PlanningVariable(
          // added machineList value range provider
          valueRangeProviderRefs = ["jobList", "machineList"],
          graphType = PlanningVariableGraphType.CHAINED
     )
     // getter for the new planning variable
     fun getPreviousStep(): ChainSuperClass {
          return previousStep
     }

     override fun getNextEntry(): Job? {
          return nextEntry
     }

     override fun setNextEntry(job: Job) {
          this.nextEntry = nextEntry
     }
}

Обратите внимание, что я добавил machineList в качестве источник возможных значений плановой переменной previousStep в качестве предварительной Важным шагом может быть либо Job в конце непустой цепочки, либо Machine, представляющий пустую цепочку.

Ваш домен очень похож на домен маршрутизации транспортных средств, где вы можете увидеть рабочий пример цепочки. При хорошем понимании принципов объединения вы сможете скопировать его на свой домен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...