From 0ec006d4235969fb6503a2f76a2ec8d6ec667f44 Mon Sep 17 00:00:00 2001 From: Saturneric Date: Tue, 20 Apr 2021 05:37:46 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E7=BC=96=E5=86=99?= =?UTF-8?q?=E5=B9=B6=E5=AE=8C=E5=96=84=E8=B5=84=E6=BA=90=E6=B1=A0=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=9C=BA=E5=88=B6;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataset_importer.py | 15 +++- model.py | 4 +- runtime.py | 190 ++++++++++++++++++++++++++++++-------------- scheduling.py | 63 ++++++++++----- 4 files changed, 190 insertions(+), 82 deletions(-) diff --git a/dataset_importer.py b/dataset_importer.py index 7d2e1ac..22b8a0e 100644 --- a/dataset_importer.py +++ b/dataset_importer.py @@ -84,8 +84,14 @@ def import_resource(res) -> Dict[str, model.Resource]: def import_resource_attributes(res, resources: Dict[str, model.Resource]): for record in res: - resources[record[0]].set_basic_attr(record[1]) - resources[record[0]].add_attr(record[2]) + resources[record[0]].set_basic_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(): @@ -133,6 +139,11 @@ def import_dataset(): import_resource_attributes(res, resources) + cur.execute("SELECT * FROM aps_process_resource;") + res = cur.fetchall() + + import_process_resource(res, processes) + conn.close() return orders, products, processes, resources diff --git a/model.py b/model.py index 3c9ca50..e1f7e9e 100644 --- a/model.py +++ b/model.py @@ -63,7 +63,7 @@ class Process: pcs_id: str, pcs_name: str, product: Product, - workspace: Workspace): + workspace: str): self.pcs_id: str = pcs_id self.pcs_name: str = pcs_name @@ -79,7 +79,7 @@ class Process: self.last_pcs: Optional[Process] = None 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]): self.prev_pcs: Process = prev_pcs diff --git a/runtime.py b/runtime.py index bd67658..85df7ee 100644 --- a/runtime.py +++ b/runtime.py @@ -1,6 +1,8 @@ +from __future__ import annotations import model from datetime import datetime, timedelta from typing import List, Dict, Optional +from copy import deepcopy class RuntimeProduct: @@ -84,36 +86,43 @@ class RuntimeProcess: # 运行时资源需求 class RuntimeResourceNeed: - def __int__(self, - process: model.Process, - resource_attr: str, - workspace: str, + def __init__(self, runtime_process: RuntimeProcess, resource_attr: str, workspace: str, start: datetime, end: datetime): - if start < end: + if start > end: 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.workspace: str = workspace self.start: datetime = start 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: - def __init__(self, resource: model.Resource): + def __init__(self, resource: model.Resource, start_time: datetime): self.resource: model.Resource = resource self.workspace: str = self.resource.workspace self.basic_attr = self.resource.basic_attr self.resource_attrs = self.resource.attrs 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 back_need: Optional[RuntimeResourceNeed] = None + if schedule.ddl < self.start_time: + return False + for resource_need in self.schedules: if resource_need.end > schedule.start: pre_need = resource_need @@ -124,21 +133,22 @@ class RuntimeResource: if pre_need is not None or back_need is not None: return False else: - self.schedules.append(schedule) - self.schedules = sorted(self.schedules, key=lambda need: need.start) + if do is True: + self.schedules.append(schedule) + self.schedules = sorted(self.schedules, key=lambda need: need.start) return True def get_earliest_available_free_times(self, duration: timedelta) -> datetime: # 没有已分配任务 - if self.schedules == 0: - return datetime.now() + if len(self.schedules) == 0: + return self.start_time # 只有一个已分配任务 - if self.schedules == 1: + if len(self.schedules) == 1: target_schedule = self.schedules[0] - if datetime.now() < target_schedule.start: - if target_schedule.start - datetime.now() > duration: + if self.start_time < target_schedule.start: + if target_schedule.start - self.start_time > duration + timedelta(minutes=1): return target_schedule.start - duration else: @@ -148,68 +158,130 @@ class RuntimeResource: pre_schedule = self.schedules[i] back_schedule = self.schedules[i + 1] - if datetime.now() < pre_schedule.start: - if pre_schedule.start - datetime.now() > duration: - return pre_schedule.start - duration + if self.start_time < pre_schedule.start: + if pre_schedule.start - self.start_time > duration + timedelta(minutes=1): + return pre_schedule.start - duration - timedelta(minutes=1) - elif back_schedule.start - pre_schedule.end > duration: + elif back_schedule.start - pre_schedule.end > duration + timedelta(minutes=1): return pre_schedule.end return self.schedules[-1].end -# 资源池 class RuntimeResourcePool: - def __init__(self, resources: List[model.Resource]): - self.pool: List[RuntimeResource] = [] + def __init__(self, resources: List[model.Resource], start_time: datetime): + self.pool: Dict[str, RuntimeResource] = {} + self.start_time: datetime = start_time for resource in resources: - runtime_resource = RuntimeResource(resource) - self.pool.append(runtime_resource) + runtime_resource = RuntimeResource(resource, self.start_time) + self.pool[runtime_resource.resource.rsc_id] = runtime_resource - def alloc_resource(self, resource_need: RuntimeResourceNeed) -> bool: - # 精确搜索 - for runtime_resource in self.pool: - # 排除不同车间 - if runtime_resource.workspace != resource_need.workspace: + # 资源分配操作 + def alloc_resource(self, resource_needs: List[RuntimeResourceNeed]): + for resource_need in resource_needs: + if resource_need.could_alloc is False: continue - if runtime_resource.basic_attr == resource_need.resource_attr: - if runtime_resource.add_schedule(resource_need) is True: - return True - # 放宽条件搜索 - for runtime_resource in self.pool: - # 排除不同车间 - if runtime_resource.workspace != resource_need.workspace: + for plan_alloc_resource_id in resource_need.plan_alloc_resources_id: + self.pool[plan_alloc_resource_id].add_schedule(resource_need, True) + + def try_alloc_resource(self, resource_needs: List[RuntimeResourceNeed]) -> bool: + + temp_pool = deepcopy(list(self.pool.values())) + + # 已经满足的需求 + fulfilled_needs = [] + + if_all_alloc = True + + for resource_need in resource_needs: + + if resource_need.could_alloc is True: 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 - # 精确搜索 - 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 + def reset_earliest_free_start_time(self, resource_needs: List[RuntimeResourceNeed]) -> bool: - # 优先利用对口资源 - if earliest_time is not None: - return earliest_time + if_found = False - # 放宽条件搜索 - for runtime_resource in self.pool: - if resource_need.resource_attr in runtime_resource.resource_attrs: - 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 + for resource_need in resource_needs: + + if resource_need.could_alloc is True: + continue + + 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 diff --git a/scheduling.py b/scheduling.py index 6b032a5..ec9cca4 100644 --- a/scheduling.py +++ b/scheduling.py @@ -2,7 +2,7 @@ import runtime import model import csv from typing import List, Dict -from datetime import datetime +from datetime import datetime, timedelta, date import time import math import dataset_importer @@ -33,27 +33,24 @@ def orders_processor(orders: Dict[str, model.Order]) -> List[runtime.RuntimeProd def search_semi_products(floor, produce_tree, produce_list, runtime_product): - runtime_semi_products = [] produce_tree.append({"runtime_product": runtime_product, "runtime_semi_products": runtime_semi_products}) # print("F", runtime_product.product.product_id, runtime_product.ddl) if len(runtime_product.product.semi_products) > 0: for item in runtime_product.product.semi_products: - runtime_semi_product = runtime.RuntimeProduct(item["semi_product"], item["amount"]) runtime_semi_product.set_ddl_start(runtime_product.ddl, runtime_product.start) # print("C", runtime_semi_product.product.product_id, runtime_semi_product.ddl) - search_semi_products(floor+1, runtime_semi_products, produce_list, runtime_semi_product) + search_semi_products(floor + 1, runtime_semi_products, produce_list, runtime_semi_product) - print("L", floor, runtime_product.product.product_id, runtime_product.ddl) + # print("L", floor, runtime_product.product.product_id, runtime_product.ddl) produce_list.append(runtime_product) def products_processor(runtime_products: List[runtime.RuntimeProduct]): - runtime_products_processes_list: List[Dict[str, any]] = [] for runtime_product in runtime_products: @@ -80,25 +77,53 @@ def products_processor(runtime_products: List[runtime.RuntimeProduct]): for item in runtime_products_processes_list: for runtime_process in item["runtimeProcess"]: 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 -# def resource_processor(resources: List[model.Resource], runtime_products_processes_list: List[Dict[str, any]]): -# resource_pool = runtime.RuntimeResourcePool(resources) -# -# for item in runtime_products_processes_list: -# ifalloc = True -# for runtime_process in item["runtimeProcess"]: -# runtime_process: runtime.RuntimeProcess = runtime_process -# for resource_item in runtime_process.process.res_needs: -# resource_item[''] -# runtime_resource_need = runtime.RuntimeResourceNeed(runtime_process.process) -# if resource_pool.alloc_resource(): +def resource_processor(runtime_products_processes_list: List[Dict[str, any]], + resource_pool: runtime.RuntimeResourcePool, + start_time: datetime): + could_alloc = True + + for item in runtime_products_processes_list: + runtime_resource_needs: List[runtime.RuntimeResourceNeed] = [] + for runtime_process in item["runtimeProcess"]: + runtime_process: runtime.RuntimeProcess = runtime_process + for resource_item in runtime_process.process.res_needs: + 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__": + + start_time: datetime = datetime.combine(date(2020, 8, 12), datetime.min.time()) + 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) - products_processor(produce_list) \ No newline at end of file + rt_rcs_list = products_processor(produce_list) + print(resource_processor(rt_rcs_list, resource_pool, start_time)) From 7b957af9e11031fcdb47d0745de36fdb1fff3523 Mon Sep 17 00:00:00 2001 From: Saturneric Date: Tue, 20 Apr 2021 12:55:57 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/runtime.py b/runtime.py index 85df7ee..4c5d379 100644 --- a/runtime.py +++ b/runtime.py @@ -124,10 +124,9 @@ class RuntimeResource: return False 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 - if back_need is not None \ - and resource_need.start < resource_need.end: + if resource_need.start < schedule.end < resource_need.end: back_need = resource_need if pre_need is not None or back_need is not None: @@ -148,7 +147,7 @@ class RuntimeResource: if len(self.schedules) == 1: target_schedule = self.schedules[0] if self.start_time < target_schedule.start: - if target_schedule.start - self.start_time > duration + timedelta(minutes=1): + if target_schedule.start - self.start_time > duration: return target_schedule.start - duration else: @@ -158,11 +157,11 @@ class RuntimeResource: pre_schedule = self.schedules[i] back_schedule = self.schedules[i + 1] - if self.start_time < pre_schedule.start: - if pre_schedule.start - self.start_time > duration + timedelta(minutes=1): - return pre_schedule.start - duration - timedelta(minutes=1) + if self.start_time < pre_schedule.start and i == 0: + if pre_schedule.start - self.start_time > duration: + return pre_schedule.start - duration - elif back_schedule.start - pre_schedule.end > duration + timedelta(minutes=1): + elif back_schedule.start - pre_schedule.end > duration: return pre_schedule.end return self.schedules[-1].end