我正在尝试编写一个飞行员排班项目,其中飞行员和副驾驶员需要被分配到航班(“任务”)。我使用python是因为客户端已有python投资。
我将此概念验证基于optapy Employee Scheduling quickstart.
当我尝试初始化我的Availability类时,代码在我的domain.py
中崩溃了。具体来说,在 Package 它的@optapy.problem_fact
装饰器中。optapy\annotations.py, line 585
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\PythonClassTranslator.java", line 292, in org.optaplanner.jpyinterpreter.PythonClassTranslator.translatePythonClass
Exception: Java Exception
The above exception was the direct cause of the following exception:
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 1220, in translate_python_class_to_java_class
out = PythonClassTranslator.translatePythonClass(python_compiled_class)
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 422, in convert_to_java_python_like_object
out = translate_python_class_to_java_class(raw_type)
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 399, in convert_to_java_python_like_object
convert_to_java_python_like_object(map_value, instance_map))
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 1193, in translate_python_class_to_java_class
static_attributes_map.put(attribute[0], convert_to_java_python_like_object(attribute[1]))
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\optapy\optaplanner_java_interop.py", line 1050, in compile_and_get_class
parent_class = translate_python_class_to_java_class(python_class).getJavaClass()
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\optapy\optaplanner_java_interop.py", line 1063, in _generate_problem_fact_class
parent_class = compile_and_get_class(python_class)
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\optapy\annotations.py", line 585, in problem_fact
out.__optapy_java_class = _generate_problem_fact_class(fact_class)
File "C:\Users\andre\Desktop\Pilot Rosterer 3000\domain.py", line 131, in <module>
class Availability:
错误如下所示:
Exception has occurred: java.lang.VerifyError (note: full exception trace is shown but execution is paused at: _run_module_as_main)
java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
org/jpyinterpreter/user/enum/Enum/__new__$$2.invoke(Lorg/jpyinterpreter/user/domain/AvailabilityType;Lorg/optaplanner/jpyinterpreter/PythonLikeObject;)Lorg/optaplanner/jpyinterpreter/PythonLikeObject; @897: invokestatic
Reason:
Type 'org/optaplanner/jpyinterpreter/PythonLikeObject' (current frame, stack[1]) is not assignable to 'org/optaplanner/jpyinterpreter/types/PythonLikeType'
Current Frame:
bci: @897
flags: { }
locals: { 'org/jpyinterpreter/user/enum/Enum/__new__$$2', 'org/jpyinterpreter/user/domain/AvailabilityType', 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/jpyinterpreter/user/domain/AvailabilityType', 'org/optaplanner/jpyinterpreter/PythonLikeObject', top, 'org/optaplanner/jpyinterpreter/types/PythonNone', top, top, top, null, 'org/optaplanner/jpyinterpreter/types/collections/PythonLikeTuple', top, top, top, top, top, 'java/lang/Object', 'org/optaplanner/jpyinterpreter/PythonLikeObject' }
stack: { 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/optaplanner/jpyinterpreter/PythonLikeObject' }
Bytecode:
0000000: b200 5a3a 0b2b 4e2c 3a04 2ac0 0002 b400
... ... ...
domain.py
import optapy
import optapy.types
import optapy.score
import datetime
import enum
class Route:
name: str
duration: int
base: str
def __init__(self, name: str = None, duration: int = None, base: str = None):
self.name = name
self.duration = duration
self.base = base
class AvailabilityType(enum.Enum):
DESIRED = 'DESIRED'
UNDESIRED = 'UNDESIRED'
UNAVAILABLE = 'UNAVAILABLE'
@staticmethod
def list():
return list(map(lambda at: at, AvailabilityType))
@optapy.problem_fact
class Pilot:
name: str
skill_set: list[str]
home_base: str
duties: list # list[Duty]
def __init__(self, name: str = None, skill_set: list[str] = None, home_base: str = None, duties: list = None):
self.name = name
self.skill_set = skill_set
self.home_base = home_base
if duties is None:
self.duties = []
else:
self.duties = duties
def get_total_hours(self): # flight hours
dt = datetime.timedelta()
for duty in self.duties:
if duty.work_type != "Flying": continue # skip non-flight duties
dt += duty.end - duty.start
return dt.seconds/3600 + dt.days*24
def get_todays_hours(self, date: datetime.date):
dt = datetime.timedelta()
for duty in self.duties:
if duty.work_type != "Flying": continue # skip non-flight duties
if duty.start.date() != date: continue # skip duties from other days
dt += duty.end - duty.start # not taking into account flights over midnight. TODO: fix
return dt.seconds/3600 + dt.days*24
def get_total_overtime_hours(self):
days = dict()
for duty in self.duties:
if duty.work_type != "Flying": continue # skip non-flight duties
day = duty.start.date()
days[day] = days.get(day, datetime.timedelta()) + duty.end - duty.start
total_overtime = datetime.timedelta()
MAX_FLIGHT_HOURS_ALLOWED = datetime.timedelta(hours=10)
for day in days:
if day > MAX_FLIGHT_HOURS_ALLOWED:
total_overtime += day - MAX_FLIGHT_HOURS_ALLOWED
return total_overtime.seconds/3600 + total_overtime.days*24
def __str__(self):
return f'Pilot(name={self.name})'
def to_dict(self):
return {
'name': self.name,
'skill_set': self.skill_set,
'home_base': self.home_base,
'duties': self.duties
}
def duty_pinning_filter(solution, duty):
return not solution.schedule_state.is_draft(duty)
@optapy.planning_entity(pinning_filter=duty_pinning_filter)
@optapy.planning_pin
class Duty:
id: int
start: datetime.datetime
end: datetime.datetime
work_type: str
detail: str
required_skill: str
pilot: Pilot
# coPilot: Pilot # REMOVED: No longer complicating duties with 2 pilots, rather just creating seperate pilot duties and copilot duties for each route. This allows us to model duties other than flights
bases: list[str] # locaiotn(s) from which the duty can be performed
def __init__(self, id: int = None, start: datetime.datetime = None, end: datetime.datetime = None, work_type: str = None, detail: str = None, required_skill: str = None, pilot: Pilot = None, bases: list[str] = None):
self.id = id
self.start = start
self.end = end
self.work_type = work_type
self.detail = detail
self.required_skill = required_skill
self.pilot = pilot
self.bases = bases
@optapy.planning_id
def get_id(self):
return self.id
def __str__(self):
return f'Duty:{self.id}\t | {self.work_type}, detail={self.detail}, pilot={self.pilot}, start={self.start}, end={self.end}, required_skill={self.required_skill}, bases={self.bases})'
def to_dict(self):
return {
'id': self.id,
'work_type': self.work_type,
'start': self.start.isoformat(),
'end': self.end.isoformat(),
'detail': self.detail,
'required_skill': self.required_skill,
'pilot': self.pilot.to_dict() if self.pilot is not None else None,
'bases': self.bases
}
# ============================================================================================
# This is to get around the circular reference problem.
# The optapy decorators cannot refence Duty/Pilot before the classes are instantiated,
# but the classes rely on eachother being instantiated first before they can instatiated
# themselves. Hence the circular reference issue.
@optapy.planning_list_variable(Duty, ['duties_list'])
def get_duties(self):
return self.duties
def set_duties(self, duties):
self.duties = duties
@optapy.planning_variable(Pilot, value_range_provider_refs=['pilot_range'])
def get_pilot(self):
return self.pilot
def set_pilot(self, pilot):
self.pilot = pilot
Pilot.get_duties = get_duties
Pilot.set_duties = set_duties
Duty.get_pilot = get_pilot
Duty.set_pilot = set_pilot
# ============================================================================================
@optapy.problem_fact
class Availability:
pilot: Pilot
date: datetime.date
availability_type: AvailabilityType
def __init__(self, pilot: Pilot = None, date: datetime.date = None, availability_type: AvailabilityType = None):
self.pilot = pilot
self.date = date
self.availability_type = availability_type
def __str__(self):
return f'Availability(pilot={self.pilot}, date={self.date}, availability_type={self.availability_type})'
def to_dict(self):
return {
'pilot': self.pilot.to_dict(),
'date': self.date.isoformat(),
'availability_type': self.availability_type.value
}
class ScheduleState:
publish_length: int
draft_length: int
first_draft_date: datetime.date
last_historic_date: datetime.date
def __init__(self, publish_length: int = None, draft_length: int = None, first_draft_date: datetime.date = None, last_historic_date: datetime.date = None):
self.publish_length = publish_length
self.draft_length = draft_length
self.first_draft_date = first_draft_date
self.last_historic_date = last_historic_date
def is_draft(self, duty):
return duty.start >= datetime.datetime.combine(self.first_draft_date, datetime.time.min)
def to_dict(self):
return {
'publish_length': self.publish_length,
'draft_length': self.draft_length,
'first_draft_date': self.first_draft_date.isoformat(),
'last_historic_date': self.last_historic_date.isoformat()
}
@optapy.planning_solution
class PilotSchedule:
schedule_state: ScheduleState
availability_list: list[Availability]
pilot_list: list[Pilot]
duty_list: list[Duty]
solver_status: optapy.types.SolverStatus
score: optapy.score.SimpleScore
def __init__(self, schedule_state, availability_list, pilot_list, duty_list, solver_status, score=None):
self.pilot_list = pilot_list
self.availability_list = availability_list
self.schedule_state = schedule_state
self.duty_list = duty_list
self.solver_status = solver_status
self.score = score
@optapy.problem_fact_collection_property(Pilot)
@optapy.value_range_provider('pilot_range')
def get_pilot_list(self):
return self.pilot_list
@optapy.problem_fact_collection_property(Availability)
def get_availability_list(self):
return self.availability_list
@optapy.planning_entity_collection_property(Duty)
def get_duty_list(self):
return self.duty_list
@optapy.planning_score(optapy.score.HardSoftScore)
def get_score(self):
return self.score
def set_score(self, score):
self.score = score
def to_dict(self):
return {
'pilot_list': list(map(lambda pilot: pilot.to_dict(), self.pilot_list)),
'availability_list': list(map(lambda availability: availability.to_dict(), self.availability_list)),
'schedule_state': self.schedule_state.to_dict(),
'duty_list': list(map(lambda duty: duty.to_dict(), self.duty_list)),
'solver_status': self.solver_status.toString(),
'score': self.score.toString(),
}
1条答案
按热度按时间n3ipq98p1#
这是Optapy的一个错误(参见https://github.com/optapy/optapy/issues/146),特别是在翻译
enum.Enum
类时可能会出现这种情况(这是由于Availability具有AvailabilityType的类型注解(枚举)。移除类型注解可能会解决此问题(由于AvailabilityType
直到求解才被转换,这是更宽容的(由于转换导致错误,它将被转换为指向CPython
类的指针))。将在问题修复后更新此答案。