|
@@ -11,7 +11,7 @@ from django.utils import timezone
|
|
|
from django.db import transaction
|
|
|
import logging
|
|
|
from rest_framework import status
|
|
|
-from .models import DeviceModel,LocationModel,LocationGroupModel,LocationContainerLink,LocationChangeLog,alloction_pre
|
|
|
+from .models import DeviceModel,LocationModel,LocationGroupModel,LocationContainerLink,LocationChangeLog,alloction_pre,base_location
|
|
|
from bound.models import BoundBatchModel,BoundDetailModel,BoundListModel
|
|
|
|
|
|
|
|
@@ -524,76 +524,53 @@ class LocationAllocation:
|
|
|
batch = container_detail.batch.bound_number
|
|
|
return batch
|
|
|
@transaction.atomic
|
|
|
- def get_location_list_remainder(self, location_list):
|
|
|
+ def get_location_list_remainder(self, location_group_list,container_code):
|
|
|
"""
|
|
|
获取可用库位的c_number列表
|
|
|
:param location_list: 库位对象列表
|
|
|
:return: 可用库位编号列表
|
|
|
"""
|
|
|
- if not location_list:
|
|
|
+ if not location_group_list:
|
|
|
return None
|
|
|
min_c_number=1000
|
|
|
min_c_number_index=1000
|
|
|
-
|
|
|
- for location in location_list:
|
|
|
- if location.status != 'available':
|
|
|
- continue
|
|
|
- if int(location.c_number)<min_c_number:
|
|
|
- min_c_number=int(location.c_number)
|
|
|
- min_c_number_index=location_list.index(location)
|
|
|
- if min_c_number_index==1000:
|
|
|
- return None
|
|
|
- else:
|
|
|
- return min_c_number_index
|
|
|
- @transaction.atomic
|
|
|
- def get_location_by_type_remainder(self, batch, layer):
|
|
|
- """
|
|
|
- 根据库位类型获取库位
|
|
|
- :param batch: 批次号
|
|
|
- :param layer: 层数限制
|
|
|
- :return: 符合条件的库位列表
|
|
|
- """
|
|
|
- # 获取符合条件的库位组并按优先级排序
|
|
|
- location_groups = LocationGroupModel.objects.filter(
|
|
|
- current_batch=batch,
|
|
|
- layer=layer,
|
|
|
- ).first()
|
|
|
-
|
|
|
- if not location_groups:
|
|
|
- return None
|
|
|
- # 合并所有库位组中的库位
|
|
|
- locations = []
|
|
|
- locations.extend(location_groups.location_items.all()) # 假设location_items是关联字段
|
|
|
-
|
|
|
- return locations if locations else None
|
|
|
- @transaction.atomic
|
|
|
- def get_location_by_type(self, location_type_list, start_location, layer):
|
|
|
- """
|
|
|
- 第一次入库,根据库位类型获取库位
|
|
|
- :param location_type_list: 库位类型列表
|
|
|
- :param start_location: 起始位置(决定优先级排序方式)
|
|
|
- :param layer: 层数限制
|
|
|
- :return: 符合条件的库位列表
|
|
|
- """
|
|
|
- # 获取符合条件的库位组并按优先级排序
|
|
|
- location_groups = LocationGroupModel.objects.filter(
|
|
|
- group_type__in=location_type_list,
|
|
|
- layer=layer,
|
|
|
- status='available'
|
|
|
+
|
|
|
+ current_task = self.get_current_finish_task(container_code)
|
|
|
+ print(f"当前已完成任务: {current_task}")
|
|
|
+ # 按压力排序
|
|
|
+ sorted_pressure = sorted(
|
|
|
+ [(0, current_task[0]), (1, current_task[1]), (2, current_task[2])],
|
|
|
+ key=lambda x: (-x[1], x[0])
|
|
|
)
|
|
|
-
|
|
|
- # 根据起始位置选择排序字段
|
|
|
- if start_location == 'in1':
|
|
|
- ordered_groups = location_groups.order_by('left_priority')
|
|
|
- elif start_location == 'in2':
|
|
|
- ordered_groups = location_groups.order_by('right_priority')
|
|
|
- else:
|
|
|
- ordered_groups = location_groups.none()
|
|
|
-
|
|
|
- locations = []
|
|
|
- locations.extend(ordered_groups.first().location_items.all())
|
|
|
- return locations if locations else None
|
|
|
-
|
|
|
+ # 交换第一和第二个元素的位置
|
|
|
+ sorted_pressure[0], sorted_pressure[1] = sorted_pressure[1], sorted_pressure[0]
|
|
|
+ print(f"任务排序: {sorted_pressure}")
|
|
|
+ print(f"当前选择:{sorted_pressure[0][0]+1}")
|
|
|
+ location_type_dict = json.loads(self.divide_solution_by_layer(location_group_list))
|
|
|
+ print(f"库位类型分配方案: {location_type_dict}")
|
|
|
+
|
|
|
+ for layer, _ in sorted_pressure:
|
|
|
+ if not location_type_dict.get(str(layer+1)):
|
|
|
+ continue
|
|
|
+ print(f"当前层: {layer+1}")
|
|
|
+ print(f"当前层库位组: {location_type_dict[str(layer+1)].keys()}")
|
|
|
+ for group_id in location_type_dict[str(layer+1)].keys():
|
|
|
+ location_group = LocationGroupModel.objects.filter(
|
|
|
+ id=group_id,
|
|
|
+ ).first()
|
|
|
+ if not location_group:
|
|
|
+ continue
|
|
|
+ location_list = location_group.location_items.filter(
|
|
|
+ status='available'
|
|
|
+ ).all().order_by('c_number')
|
|
|
+ if not location_list:
|
|
|
+ print(f"当前层库位组 {location_group.group_code} 可用库位: None")
|
|
|
+ continue
|
|
|
+ # 提取所有库位的 c_number
|
|
|
+ c_numbers = [loc.c_number for loc in location_list]
|
|
|
+ print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
|
|
|
+ return location_list[0]
|
|
|
+
|
|
|
def get_location_type(self, container_code):
|
|
|
"""
|
|
|
智能库位分配核心算法
|
|
@@ -617,7 +594,7 @@ class LocationAllocation:
|
|
|
current_pressure = self.get_current_pressure()
|
|
|
|
|
|
# 测试参数
|
|
|
- # total_pallets = 3
|
|
|
+ total_pallets = 30
|
|
|
# layer_capacity = [{'T1': 29, 'T2': 14, 'S4': 10, 'T4': 27, 'T5': 27}, {'T1': 0, 'T2': 0, 'S4': 0, 'T4': 0, 'T5': 21}, {'T1': 29, 'T2': 14, 'S4': 10, 'T4': 27, 'T5': 27}]
|
|
|
# current_pressure = [1,0,0]
|
|
|
print(f"[1]托盘数目: {total_pallets}")
|
|
@@ -627,10 +604,10 @@ class LocationAllocation:
|
|
|
# 定义库位容量表
|
|
|
LOCATION_CAPACITY = {'T1':1, 'T2':2, 'S4':4, 'T4':4, 'T5':5}
|
|
|
|
|
|
- def allocate(remain, path, pressure,layer_capacity_state, depth=0):
|
|
|
+ def allocate(remain, path, pressure,real_pressure,layer_capacity_state, depth=0):
|
|
|
# 终止条件
|
|
|
if remain <= 0:
|
|
|
- return [path,pressure]
|
|
|
+ return [path,real_pressure]
|
|
|
# 深拷贝当前层容量状态
|
|
|
new_layer_capacity = copy.deepcopy(layer_capacity_state)
|
|
|
# print(f"[2]当前剩余: {new_layer_capacity}")
|
|
@@ -669,11 +646,15 @@ class LocationAllocation:
|
|
|
if effective_cap <= remain:
|
|
|
new_remain = remain - effective_cap
|
|
|
new_pressure = pressure.copy()
|
|
|
- new_pressure[layer] += effective_cap # 按实际存放数计算压力
|
|
|
+ for i in range(0, 3):
|
|
|
+ new_pressure[i] -=1 if new_pressure[i] > 0 else 0
|
|
|
+ new_pressure[layer] += effective_cap # 按实际存放数计算压力,此时别的楼层压力可能降下来了
|
|
|
+ real_pressure[layer] += effective_cap # 实际压力
|
|
|
result = allocate(
|
|
|
new_remain,
|
|
|
path + [f"{layer+1}_{loc_type}"],
|
|
|
new_pressure,
|
|
|
+ real_pressure,
|
|
|
updated_capacity,
|
|
|
depth + 1
|
|
|
)
|
|
@@ -684,7 +665,7 @@ class LocationAllocation:
|
|
|
return None
|
|
|
|
|
|
# 执行分配
|
|
|
- allocation = allocate(total_pallets, [], [current_pressure[0], current_pressure[1],current_pressure[2]], layer_capacity)
|
|
|
+ allocation = allocate(total_pallets, [], [current_pressure[0], current_pressure[1],current_pressure[2]],[current_pressure[0], current_pressure[1],current_pressure[2]], layer_capacity)
|
|
|
|
|
|
if not allocation:
|
|
|
logger.error("无法生成有效分配方案")
|
|
@@ -695,13 +676,15 @@ class LocationAllocation:
|
|
|
print(f"[6]分配方案: {allocation_json}")
|
|
|
solution = alloction_pre(
|
|
|
batch_number=batch,
|
|
|
- layer_pre_type =allocation_json,
|
|
|
- layer1_pressure=allocation[1][0],
|
|
|
- layer2_pressure=allocation[1][1],
|
|
|
- layer3_pressure=allocation[1][2],
|
|
|
+ layer_pre_type =allocation_json
|
|
|
)
|
|
|
-
|
|
|
+ solution_pressure = base_location.objects.get(id=1)
|
|
|
+ solution_pressure.layer1_pressure = allocation[1][0]
|
|
|
+ solution_pressure.layer2_pressure = allocation[1][1]
|
|
|
+ solution_pressure.layer3_pressure = allocation[1][2]
|
|
|
+
|
|
|
solution.save()
|
|
|
+ solution_pressure.save()
|
|
|
|
|
|
return [loc.split('_')[1] for loc in allocation[0]]
|
|
|
|
|
@@ -737,20 +720,87 @@ class LocationAllocation:
|
|
|
|
|
|
def get_current_pressure(self):
|
|
|
"""获取实时工作压力"""
|
|
|
- last_solution = alloction_pre.objects.order_by('-id').first()
|
|
|
+ last_solution = base_location.objects.order_by('-id').first()
|
|
|
+ if not last_solution:
|
|
|
+ base_location.objects.create(
|
|
|
+ layer1_pressure=0,
|
|
|
+ layer2_pressure=0,
|
|
|
+ layer3_pressure=0,
|
|
|
+ ).save()
|
|
|
return [
|
|
|
last_solution.layer1_pressure if last_solution else 0,
|
|
|
last_solution.layer2_pressure if last_solution else 0,
|
|
|
last_solution.layer3_pressure if last_solution else 0,
|
|
|
]
|
|
|
+ def get_current_finish_task(self,container):
|
|
|
+ batch = self.get_batch(container)
|
|
|
+
|
|
|
+ if not batch:
|
|
|
+ return None
|
|
|
+ solution = alloction_pre.objects.filter(batch_number=batch).first()
|
|
|
+ if not solution:
|
|
|
+ return None
|
|
|
+
|
|
|
+ return [solution.layer1_task_finish_number,solution.layer2_task_finish_number,solution.layer3_task_finish_number]
|
|
|
|
|
|
@transaction.atomic
|
|
|
- def get_location_by_status(self,container_code,start_location,layer):
|
|
|
+ def get_location_by_type(self, location_type_list, start_location, container_code):
|
|
|
+ """
|
|
|
+ 根据库位类型获取库位,先根据工作压力找出最空闲的层,看下这层有没有工作,如果有,就从里面找,如果没有,则跳过该层,继续找下一层
|
|
|
+ :param location_type_list: 库位分配方案
|
|
|
+ {
|
|
|
+ "1": {
|
|
|
+ "S4": 1
|
|
|
+ },
|
|
|
+ "2": {},
|
|
|
+ "3": {
|
|
|
+ "T5": 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ :param start_location: 起始位置(决定优先级排序方式)
|
|
|
+ :return: 符合条件的库位列表
|
|
|
+ """
|
|
|
+ locations = []
|
|
|
+
|
|
|
+ for layer, location_type_dict in location_type_list.items():
|
|
|
+
|
|
|
+ if not location_type_dict:
|
|
|
+ continue
|
|
|
+ # 获取库位类型列表
|
|
|
+ location_type = list(location_type_dict.keys())
|
|
|
+ demand_number = sum(location_type_dict.values())
|
|
|
+ print (f"层{layer} 需求数量: {demand_number}, 库位: {location_type}")
|
|
|
+ location_groups = LocationGroupModel.objects.filter(
|
|
|
+ group_type__in=location_type,
|
|
|
+ layer=layer,
|
|
|
+ status='available'
|
|
|
+ )
|
|
|
+ if not location_groups:
|
|
|
+ print(f"层{layer} 无库位")
|
|
|
+ # 根据起始位置选择排序字段
|
|
|
+ if start_location == 'in1':
|
|
|
+ ordered_groups = location_groups.order_by('left_priority')
|
|
|
+ elif start_location == 'in2':
|
|
|
+ ordered_groups = location_groups.order_by('right_priority')
|
|
|
+ else:
|
|
|
+ ordered_groups = location_groups.none()
|
|
|
+
|
|
|
+ number = 0
|
|
|
+ for location_group in ordered_groups:
|
|
|
+ if number >= demand_number:
|
|
|
+ break
|
|
|
+ locations.append(f"{layer}_{location_group.id}")
|
|
|
+ number += 1
|
|
|
+
|
|
|
+ return locations if locations else None
|
|
|
+
|
|
|
+
|
|
|
+ @transaction.atomic
|
|
|
+ def get_location_by_status(self,container_code,start_location):
|
|
|
"""
|
|
|
根据库位状态获取库位
|
|
|
:param location_type: 库位类型
|
|
|
:param start_location: 起始库位 if in1 优先考虑left_priority, if in2 优先考虑right_priority 就是获取库位组列表之后进行排序
|
|
|
- :param layer: 层数 限定层数
|
|
|
:return: 库位列表
|
|
|
"""
|
|
|
# 1. 获取批次状态 1 为已组盘 2 为部分入库 3 为全部入库
|
|
@@ -759,35 +809,29 @@ class LocationAllocation:
|
|
|
if status == 1:
|
|
|
# 2. 获取库位组
|
|
|
print (f"第一次入库")
|
|
|
- location_type_list = self.get_location_type(container_code)
|
|
|
- print(f"库位类型:{location_type_list}")
|
|
|
- location_list = self.get_location_by_type(location_type_list,start_location,layer)
|
|
|
- print(f"库位列表:{location_list}")
|
|
|
- location_min_index = self.get_location_list_remainder(location_list)
|
|
|
- print(f"库位安排到第{location_min_index+1}个库位:{location_list[location_min_index]}")
|
|
|
- if not location_list[location_min_index]:
|
|
|
- # 库位已满,返回None
|
|
|
- return None
|
|
|
- else:
|
|
|
+ self.get_location_type(container_code)
|
|
|
+ location_type_list = json.loads(alloction_pre.objects.filter(batch_number=self.get_batch(container_code)).first().layer_pre_type)
|
|
|
+ print(f"库组分配方案:{location_type_list}")
|
|
|
+ location_list = self.get_location_by_type(location_type_list,start_location,container_code)
|
|
|
+ print(f"库组列表:{location_list}")
|
|
|
+ # 预定这些库组
|
|
|
+
|
|
|
+ location_min_value = self.get_location_list_remainder(location_list,container_code)
|
|
|
+ print(f"库位安排到第{location_min_value.c_number}个库位:{location_min_value}")
|
|
|
+ # if not location_list[location_min_index]:
|
|
|
+ # # 库位已满,返回None
|
|
|
+ # return None
|
|
|
+ # else:
|
|
|
|
|
|
- return location_list[location_min_index]
|
|
|
+ # return location_list[location_min_index]
|
|
|
+ return 1
|
|
|
+
|
|
|
elif status == 2:
|
|
|
- print(f"部分入库")
|
|
|
- # 2. 获取库位组
|
|
|
- location_list = self.get_location_by_type_remainder(self.get_batch(container_code),layer)
|
|
|
+ # 3. 获取部分入库库位
|
|
|
+ print (f"部分入库")
|
|
|
+ location_list = self.get_location_by_status_part(container_code,start_location)
|
|
|
print(f"库位列表:{location_list}")
|
|
|
- location_min_index = self.get_location_list_remainder(location_list)
|
|
|
- print(f"库位安排到第{location_min_index+1}个库位:{location_list[location_min_index]}")
|
|
|
- if not location_list[location_min_index]:
|
|
|
- # 库位已满,返回None
|
|
|
- return None
|
|
|
- else:
|
|
|
- return location_list[location_min_index]
|
|
|
- elif status == 3:
|
|
|
- print(f"全部入库")
|
|
|
- # 2. 获取库位组
|
|
|
- location_list = self.get_location_by_type_remainder(self.get_batch(container_code),layer)
|
|
|
- return location_list
|
|
|
+
|
|
|
|
|
|
|
|
|
|