Merge remote-tracking branch 'origin/master'

# Conflicts:
#	scheduling.py
This commit is contained in:
Saturneric 2021-04-20 21:19:31 +08:00
commit 5950c1dab9
4 changed files with 188 additions and 78 deletions

View File

@ -84,8 +84,14 @@ def import_resource(res) -> Dict[str, model.Resource]:
def import_resource_attributes(res, resources: Dict[str, model.Resource]): def import_resource_attributes(res, resources: Dict[str, model.Resource]):
for record in res: for record in res:
resources[record[0]].set_basic_attr(record[1]) resources[record[0]].set_basic_attr(record[2])
resources[record[0]].add_attr(record[2]) for attr in record[1].split(","):
resources[record[0]].add_attr(attr)
def import_process_resource(res, processes: Dict[str, model.Process]):
for record in res:
processes[record[0]].add_res_need(record[1], record[2])
def import_dataset(): def import_dataset():
@ -133,6 +139,11 @@ def import_dataset():
import_resource_attributes(res, resources) import_resource_attributes(res, resources)
cur.execute("SELECT * FROM aps_process_resource;")
res = cur.fetchall()
import_process_resource(res, processes)
conn.close() conn.close()
return orders, products, processes, resources return orders, products, processes, resources

View File

@ -63,7 +63,7 @@ class Process:
pcs_id: str, pcs_id: str,
pcs_name: str, pcs_name: str,
product: Product, product: Product,
workspace: Workspace): workspace: str):
self.pcs_id: str = pcs_id self.pcs_id: str = pcs_id
self.pcs_name: str = pcs_name self.pcs_name: str = pcs_name
@ -79,7 +79,7 @@ class Process:
self.last_pcs: Optional[Process] = None self.last_pcs: Optional[Process] = None
self.res_needs: List[Dict[str, any]] = [] self.res_needs: List[Dict[str, any]] = []
self.workspace: Workspace = workspace self.workspace: str = workspace
def set_pre_last_pcs(self, prev_pcs: Optional[Process], last_pcs: Optional[Process]): def set_pre_last_pcs(self, prev_pcs: Optional[Process], last_pcs: Optional[Process]):
self.prev_pcs: Process = prev_pcs self.prev_pcs: Process = prev_pcs

View File

@ -1,6 +1,8 @@
from __future__ import annotations
import model import model
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import List, Dict, Optional from typing import List, Dict, Optional
from copy import deepcopy
class RuntimeProduct: class RuntimeProduct:
@ -84,61 +86,68 @@ class RuntimeProcess:
# 运行时资源需求 # 运行时资源需求
class RuntimeResourceNeed: class RuntimeResourceNeed:
def __int__(self, def __init__(self, runtime_process: RuntimeProcess, resource_attr: str, workspace: str,
process: model.Process,
resource_attr: str,
workspace: str,
start: datetime, end: datetime): start: datetime, end: datetime):
if start < end: if start > end:
raise RuntimeError("the start time must before the end time") raise RuntimeError("the start time must before the end time")
self.process: model.Process = process self.process: model.Process = runtime_process.process
self.resource_attr: str = resource_attr self.resource_attr: str = resource_attr
self.workspace: str = workspace self.workspace: str = workspace
self.start: datetime = start self.start: datetime = start
self.end: datetime = end self.end: datetime = end
self.plan_alloc_resources_id: List[str] = []
self.could_alloc = False
self.ddl = runtime_process.ddl
def add_plan_alloc_resource(self, runtime_resource_id: str):
self.plan_alloc_resources_id.append(runtime_resource_id)
# 运行时资源 # 运行时资源
class RuntimeResource: class RuntimeResource:
def __init__(self, resource: model.Resource): def __init__(self, resource: model.Resource, start_time: datetime):
self.resource: model.Resource = resource self.resource: model.Resource = resource
self.workspace: str = self.resource.workspace self.workspace: str = self.resource.workspace
self.basic_attr = self.resource.basic_attr self.basic_attr = self.resource.basic_attr
self.resource_attrs = self.resource.attrs self.resource_attrs = self.resource.attrs
self.schedules: List[RuntimeResourceNeed] = [] self.schedules: List[RuntimeResourceNeed] = []
self.start_time: datetime = start_time
def add_schedule(self, schedule: RuntimeResourceNeed) -> bool: def add_schedule(self, schedule: RuntimeResourceNeed, do: bool) -> bool:
pre_need: Optional[RuntimeResourceNeed] = None pre_need: Optional[RuntimeResourceNeed] = None
back_need: Optional[RuntimeResourceNeed] = None back_need: Optional[RuntimeResourceNeed] = None
if schedule.ddl < self.start_time:
return False
for resource_need in self.schedules: for resource_need in self.schedules:
if resource_need.end > schedule.start: if resource_need.end > schedule.start > resource_need.start:
pre_need = resource_need pre_need = resource_need
if back_need is not None \ if resource_need.start < schedule.end < resource_need.end:
and resource_need.start < resource_need.end:
back_need = resource_need back_need = resource_need
if pre_need is not None or back_need is not None: if pre_need is not None or back_need is not None:
return False return False
else: else:
self.schedules.append(schedule) if do is True:
self.schedules = sorted(self.schedules, key=lambda need: need.start) self.schedules.append(schedule)
self.schedules = sorted(self.schedules, key=lambda need: need.start)
return True return True
def get_earliest_available_free_times(self, duration: timedelta) -> datetime: def get_earliest_available_free_times(self, duration: timedelta) -> datetime:
# 没有已分配任务 # 没有已分配任务
if self.schedules == 0: if len(self.schedules) == 0:
return datetime.now() return self.start_time
# 只有一个已分配任务 # 只有一个已分配任务
if self.schedules == 1: if len(self.schedules) == 1:
target_schedule = self.schedules[0] target_schedule = self.schedules[0]
if datetime.now() < target_schedule.start: if self.start_time < target_schedule.start:
if target_schedule.start - datetime.now() > duration: if target_schedule.start - self.start_time > duration:
return target_schedule.start - duration return target_schedule.start - duration
else: else:
@ -148,8 +157,8 @@ class RuntimeResource:
pre_schedule = self.schedules[i] pre_schedule = self.schedules[i]
back_schedule = self.schedules[i + 1] back_schedule = self.schedules[i + 1]
if datetime.now() < pre_schedule.start: if self.start_time < pre_schedule.start and i == 0:
if pre_schedule.start - datetime.now() > duration: if pre_schedule.start - self.start_time > duration:
return pre_schedule.start - duration return pre_schedule.start - duration
elif back_schedule.start - pre_schedule.end > duration: elif back_schedule.start - pre_schedule.end > duration:
@ -158,58 +167,120 @@ class RuntimeResource:
return self.schedules[-1].end return self.schedules[-1].end
# 资源池
class RuntimeResourcePool: class RuntimeResourcePool:
def __init__(self, resources: List[model.Resource]): def __init__(self, resources: List[model.Resource], start_time: datetime):
self.pool: List[RuntimeResource] = [] self.pool: Dict[str, RuntimeResource] = {}
self.start_time: datetime = start_time
for resource in resources: for resource in resources:
runtime_resource = RuntimeResource(resource) runtime_resource = RuntimeResource(resource, self.start_time)
self.pool.append(runtime_resource) self.pool[runtime_resource.resource.rsc_id] = runtime_resource
def alloc_resource(self, resource_need: RuntimeResourceNeed) -> bool: # 资源分配操作
# 精确搜索 def alloc_resource(self, resource_needs: List[RuntimeResourceNeed]):
for runtime_resource in self.pool: for resource_need in resource_needs:
# 排除不同车间 if resource_need.could_alloc is False:
if runtime_resource.workspace != resource_need.workspace:
continue continue
if runtime_resource.basic_attr == resource_need.resource_attr: for plan_alloc_resource_id in resource_need.plan_alloc_resources_id:
if runtime_resource.add_schedule(resource_need) is True: self.pool[plan_alloc_resource_id].add_schedule(resource_need, True)
return True
# 放宽条件搜索 def try_alloc_resource(self, resource_needs: List[RuntimeResourceNeed]) -> bool:
for runtime_resource in self.pool:
# 排除不同车间 temp_pool = deepcopy(list(self.pool.values()))
if runtime_resource.workspace != resource_need.workspace:
# 已经满足的需求
fulfilled_needs = []
if_all_alloc = True
for resource_need in resource_needs:
if resource_need.could_alloc is True:
continue continue
if resource_need.resource_attr in runtime_resource.resource_attrs:
if runtime_resource.add_schedule(resource_need) is True:
return True
return False # 精确搜索
for runtime_resource in temp_pool:
# 排除不同车间
if runtime_resource.workspace != resource_need.workspace:
continue
if runtime_resource.basic_attr == resource_need.resource_attr:
if runtime_resource.add_schedule(resource_need, False) is True:
resource_need.add_plan_alloc_resource(runtime_resource.resource.rsc_id)
fulfilled_needs.append(resource_need)
runtime_resource.add_schedule(resource_need, True)
resource_need.could_alloc = True
break
def get_earliest_free_time(self, resource_need: RuntimeResourceNeed) -> datetime: # 是否已经分配完成
if resource_need.could_alloc is False:
# 放宽条件搜索
for runtime_resource in temp_pool:
# 排除不同车间
if runtime_resource.workspace != resource_need.workspace:
continue
if resource_need.resource_attr in runtime_resource.resource_attrs:
if runtime_resource.add_schedule(resource_need, False) is True:
resource_need.add_plan_alloc_resource(runtime_resource.resource.rsc_id)
fulfilled_needs.append(resource_need)
runtime_resource.add_schedule(resource_need, True)
resource_need.could_alloc = True
break
earliest_time: Optional[datetime] = None if resource_need not in fulfilled_needs:
if_all_alloc = False
duration = resource_need.end - resource_need.start return if_all_alloc
# 精确搜索 def reset_earliest_free_start_time(self, resource_needs: List[RuntimeResourceNeed]) -> bool:
for runtime_resource in self.pool:
if runtime_resource.basic_attr == resource_need.resource_attr:
temp_earliest_time = runtime_resource.get_earliest_available_free_times(duration)
if earliest_time is None or earliest_time > temp_earliest_time:
earliest_time = temp_earliest_time
# 优先利用对口资源 if_found = False
if earliest_time is not None:
return earliest_time
# 放宽条件搜索 for resource_need in resource_needs:
for runtime_resource in self.pool:
if resource_need.resource_attr in runtime_resource.resource_attrs: if resource_need.could_alloc is True:
temp_earliest_time = runtime_resource.get_earliest_available_free_times(duration) continue
if earliest_time is None or earliest_time > temp_earliest_time:
earliest_time = temp_earliest_time earliest_time: Optional[datetime] = None
duration = resource_need.end - resource_need.start
# 精确搜索
for runtime_resource in self.pool.values():
# 排除不同车间
if runtime_resource.workspace != resource_need.workspace:
continue
if runtime_resource.basic_attr == resource_need.resource_attr:
temp_earliest_time = runtime_resource.get_earliest_available_free_times(duration)
# 时间不能超过DDL
if temp_earliest_time > resource_need.ddl - duration:
continue
if earliest_time is None or earliest_time > temp_earliest_time:
earliest_time = temp_earliest_time
# 优先利用对口资源
if earliest_time is not None:
resource_need.start = earliest_time
# 放宽条件搜索
else:
for runtime_resource in self.pool.values():
# 排除不同车间
if runtime_resource.workspace != resource_need.workspace:
continue
if resource_need.resource_attr in runtime_resource.resource_attrs:
temp_earliest_time = runtime_resource.get_earliest_available_free_times(duration)
# 时间不能超过DDL
if temp_earliest_time > resource_need.ddl - duration:
continue
if earliest_time is None or earliest_time > temp_earliest_time:
earliest_time = temp_earliest_time
if earliest_time is None:
if_found = False
else:
resource_need.start = earliest_time
resource_need.end = earliest_time + duration
if_found = True
return if_found
return earliest_time

View File

@ -2,7 +2,7 @@ import runtime
import model import model
import csv import csv
from typing import List, Dict from typing import List, Dict
from datetime import datetime from datetime import datetime, timedelta, date
import time import time
import math import math
import dataset_importer import dataset_importer
@ -81,25 +81,53 @@ def products_processor(runtime_products: List[runtime.RuntimeProduct]):
for item in runtime_products_processes_list: for item in runtime_products_processes_list:
for runtime_process in item["runtimeProcess"]: for runtime_process in item["runtimeProcess"]:
runtime_product: runtime.RuntimeProduct = item["runtimeProduct"] runtime_product: runtime.RuntimeProduct = item["runtimeProduct"]
print(runtime_product.product.product_id, runtime_product.delay, runtime_process.process.pcs_id) # print(runtime_product.product.product_id, runtime_product.delay, runtime_process.process.pcs_id)
return runtime_products_processes_list return runtime_products_processes_list
# def resource_processor(resources: List[model.Resource], runtime_products_processes_list: List[Dict[str, any]]): def resource_processor(runtime_products_processes_list: List[Dict[str, any]],
# resource_pool = runtime.RuntimeResourcePool(resources) resource_pool: runtime.RuntimeResourcePool,
# start_time: datetime):
# for item in runtime_products_processes_list: could_alloc = True
# ifalloc = True
# for runtime_process in item["runtimeProcess"]: for item in runtime_products_processes_list:
# runtime_process: runtime.RuntimeProcess = runtime_process runtime_resource_needs: List[runtime.RuntimeResourceNeed] = []
# for resource_item in runtime_process.process.res_needs: for runtime_process in item["runtimeProcess"]:
# resource_item[''] runtime_process: runtime.RuntimeProcess = runtime_process
# runtime_resource_need = runtime.RuntimeResourceNeed(runtime_process.process) for resource_item in runtime_process.process.res_needs:
# if resource_pool.alloc_resource(): resource_attr = resource_item["rcs_attr"]
amount = resource_item["amount"]
for i in range(amount):
runtime_resource_need: runtime.RuntimeResourceNeed = runtime.RuntimeResourceNeed(
runtime_process,
resource_attr,
runtime_process.process.workspace,
start_time,
start_time + timedelta(minutes=runtime_process.process.pdt_time))
runtime_resource_needs.append(runtime_resource_need)
if resource_pool.try_alloc_resource(runtime_resource_needs):
resource_pool.alloc_resource(runtime_resource_needs)
else:
while resource_pool.reset_earliest_free_start_time(runtime_resource_needs):
resource_pool.try_alloc_resource(runtime_resource_needs)
resource_pool.alloc_resource(runtime_resource_needs)
for runtime_resource_need in runtime_resource_needs:
if runtime_resource_need.could_alloc is False:
could_alloc = False
break
return could_alloc
if __name__ == "__main__": if __name__ == "__main__":
start_time: datetime = datetime.combine(date(2020, 8, 12), datetime.min.time())
m_orders, m_products, m_processes, m_resources = dataset_importer.import_dataset() m_orders, m_products, m_processes, m_resources = dataset_importer.import_dataset()
resource_pool: runtime.RuntimeResourcePool = runtime.RuntimeResourcePool(m_resources.values(), start_time)
produce_list = orders_processor(m_orders) produce_list = orders_processor(m_orders)
products_processor(produce_list) rt_rcs_list = products_processor(produce_list)
print(resource_processor(rt_rcs_list, resource_pool, start_time))