123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- from django.db import transaction
- from .queries import LocationQueries
- from .updates import LocationUpdates
- from .algorithms import AllocationAlgorithm
- import logging
- import json
- from collections import defaultdict
- from .models import *
- from bound.models import BoundBatchModel, BoundDetailModel, OutBatchModel, BoundListModel
- import datetime
- from django.utils import timezone
- logger = logging.getLogger(__name__)
- class AllocationService:
- @classmethod
- @transaction.atomic
- def allocate(cls, container_code, start_point):
- logger.info(f"请求托盘:{container_code},请求位置:{start_point}")
- batch_info = LocationQueries.get_batch_info(container_code)
- if not batch_info :
- raise ValueError("无效的容器或批次信息")
- if not batch_info['number']:
- if batch_info['class'] == 2:
- return cls._container_group_allocation(container_code,batch_info, start_point)
- else:
- return cls._container_scattered_allocation(container_code,batch_info, start_point)
- # if batch_info['status'] == 1 or batch_info['status'] == 0:
- # return cls._first_allocation(container_code, batch_info, start_point)
- # elif batch_info['status'] == 2:
- return cls._subsequent_allocation(container_code, batch_info, start_point)
- # else:
- # raise ValueError("非法的批次状态")
- @classmethod
- @transaction.atomic
- def _first_allocation(cls, container_code, batch_info, start_point):
- total , scatter_total = LocationQueries.get_pallet_count_by_batch(container_code)
- current_total = LocationQueries.get_pallet_count(container_code)
- if not total:
- raise ValueError("无效的容器或批次信息")
- LocationUpdates.update_pallet_count(total, batch_info['number'])
- if current_total == 0:
-
- layer_cap = LocationQueries.get_group_capacity()
- pressure = LocationQueries.get_current_pressure()
- solution, new_pressure = AllocationAlgorithm.generate_plan(total-scatter_total, layer_cap, pressure)
-
- if not solution:
- raise RuntimeError("无法生成有效分配方案")
- formatted = json.loads(AllocationAlgorithm.format_solution(solution))
- LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
- # 使用距离最近的库位,增加切换
- locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
- return cls._execute_allocation(locations, start_point,container_code,batch_info)
- else:
- return cls._subsequent_allocation(container_code, batch_info, start_point)
- # locations = LocationQueries.get_left_location_group(batch_info['number'])
- # if not locations:
- # layer_cap = LocationQueries.get_group_capacity()
- # pressure = LocationQueries.get_current_pressure()
- # finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
- # solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum, layer_cap, pressure)
-
- # if not solution:
- # raise RuntimeError("无法生成有效分配方案")
- # formatted = json.loads(AllocationAlgorithm.format_solution(solution))
- # LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
- # # 使用距离最近的库位,增加切换
- # locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
- # return cls._execute_allocation(locations, start_point,container_code,batch_info)
- # else:
- # return cls._execute_allocation(locations, start_point,container_code,batch_info)
- @classmethod
- def _container_group_allocation(cls,container_code, batch_info, start_point):
- total = 1
- batch_info['number'] = 'ContainerGroup'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
- if not total:
- raise ValueError("无效的容器或批次信息")
-
- layer_cap = LocationQueries.get_group_capacity()
- pressure = LocationQueries.get_current_pressure()
- solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
- if not solution:
- raise RuntimeError("无法生成有效分配方案")
- formatted = json.loads(AllocationAlgorithm.format_solution(solution))
- LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
- # 使用距离最近的库位,增加切换
- locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
- return cls._execute_allocation_no_batch(locations, start_point,container_code,batch_info)
-
- @classmethod
- def _container_scattered_allocation(cls,container_code, batch_info, start_point):
- total = 1
- batch_info['number'] = 'ContainerScattered'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
- layer_cap = LocationQueries.get_group_capacity()
- pressure = LocationQueries.get_current_pressure()
- solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
- if not solution:
- raise RuntimeError("无法生成有效分配方案")
- formatted = json.loads(AllocationAlgorithm.format_solution(solution))
- LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
- # 使用距离最近的库位,增加切换
- locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
- return cls._execute_allocation(locations, start_point,container_code,batch_info)
- @classmethod
- def _execute_allocation_no_batch(cls, solution, start_point,container_code,batch_info):
- LocationUpdates.update_group_status_reserved(solution)
- print(f"[0] 库位组状态预定成功!")
- location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
- if not location_min_value:
- raise ValueError("[1] 任务分配失败,无可用库位")
- print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
- location_min_value.status = 'reserved'
- location_min_value.save()
- print(f"[2] 库位状态更新成功!")
- update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
- if not update_location_group_status:
- raise ValueError("[3] 库位组状态更新失败")
- print(f"[3] 库位组状态更新成功!")
- update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
- if not update_location_group_batch:
- raise ValueError("[5] 批次信息更新失败")
- print(f"[5] 批次信息更新成功!")
- update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
- if not update_location_container_link:
- raise ValueError("[6] 容器与库位关联失败")
- print(f"[6] 容器与库位关联成功!")
-
- update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
- if not update_location_container_detail:
- raise ValueError("[7] 托盘详情状态更新失败")
- print(f"[7] 托盘详情状态更新成功!")
- allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
-
- return location_min_value,allocation_target_location, batch_info
- @classmethod
- def _execute_allocation(cls, solution, start_point,container_code,batch_info):
- LocationUpdates.update_group_status_reserved(solution)
- print(f"[0] 库位组状态预定成功!")
- location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
- if not location_min_value:
- raise ValueError("[1] 任务分配失败,无可用库位")
- print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
- location_min_value.status = 'reserved'
- location_min_value.save()
- print(f"[2] 库位状态更新成功!")
- update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
- if not update_location_group_status:
- raise ValueError("[3] 库位组状态更新失败")
- print(f"[3] 库位组状态更新成功!")
- update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
- if not update_batch_status:
- raise ValueError("[4] 批次状态更新失败")
- print(f"[4] 批次状态更新成功!")
- update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
- if not update_location_group_batch:
- raise ValueError("[5] 批次信息更新失败")
- print(f"[5] 批次信息更新成功!")
- update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
- if not update_location_container_link:
- raise ValueError("[6] 容器与库位关联失败")
- print(f"[6] 容器与库位关联成功!")
-
- update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
- if not update_location_container_detail:
- raise ValueError("[7] 托盘详情状态更新失败")
- print(f"[7] 托盘详情状态更新成功!")
- allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
-
- return location_min_value,allocation_target_location, batch_info
- @classmethod
- def _move_allocation(cls, start_point,target_point,container_code):
- batch_info = LocationQueries.get_batch_info(container_code)
- start_location = AllocationAlgorithm.generate_WMS_location(start_point)
- target_location = AllocationAlgorithm.generate_WMS_location(target_point)
- if not start_location or not target_location:
- raise ValueError("无效的起始或目标位置")
- if start_location.location_code == target_location.location_code:
- raise ValueError("起始位置与目标位置相同")
-
- location_min_value = AllocationService.get_location_group_remainder(target_location.location_group)
- if not location_min_value:
- raise ValueError("[1] 任务分配失败,无可用库位")
- print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
- start_location.status = 'available'
- start_location.save()
-
- location_min_value.status = 'occupied'
- location_min_value.save()
- print(f"[2] 库位状态更新成功!")
- update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
- update_start_location_group_status= LocationUpdates.update_location_group_status(start_location.location_code)
- if not update_location_group_status or not update_start_location_group_status:
- raise ValueError("[3] 库位组状态更新失败")
- print(f"[3] 库位组状态更新成功!")
- # update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
- # if not update_batch_status:
- # raise ValueError("[4] 批次状态更新失败")
- # print(f"[4] 批次状态更新成功!")
- update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
- if not update_location_group_batch:
- raise ValueError("[5] 批次信息更新失败")
- print(f"[5] 批次信息更新成功!")
- update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
- if not update_location_container_link:
- raise ValueError("[6] 容器与库位关联失败")
- print(f"[6] 容器与库位关联成功!")
-
- update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
- if not update_location_container_detail:
- raise ValueError("[7] 托盘详情状态更新失败")
- print(f"[7] 托盘详情状态更新成功!")
- allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
-
- return location_min_value,allocation_target_location, batch_info
- @classmethod
- def _subsequent_allocation(cls, container_code, batch_info, start_point):
- total , scatter_total = LocationQueries.get_pallet_count_by_batch(container_code)
- LocationUpdates.update_pallet_count(total, batch_info['number'])
- locations = LocationQueries.get_left_location_group(batch_info['number'])
- if not locations:
- layer_cap = LocationQueries.get_group_capacity()
- pressure = LocationQueries.get_current_pressure()
- finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
- print(f"当前已完成任务: {finish_task_sum}, 总任务数: {total},散盘任务数: {scatter_total}")
- if total-finish_task_sum-scatter_total == 0:
- total += 1
- solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum-scatter_total, layer_cap, pressure)
- if not solution:
- raise RuntimeError("无法生成有效分配方案")
- formatted = json.loads(AllocationAlgorithm.format_solution(solution))
- LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
- # 使用距离最近的库位,增加切换
- locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
- return cls._execute_allocation(locations, start_point,container_code,batch_info)
- else:
- return cls._execute_allocation(locations, start_point,container_code,batch_info)
- @transaction.atomic
- def get_location_list_remainder(location_group_list,container_code,batch_info):
- """
- 获取可用库位的c_number列表
- :param location_list: 库位对象列表
- :return: 可用库位编号列表
- """
- if not location_group_list:
- return None
- current_task = LocationQueries.get_current_finish_task(container_code,batch_info)
- print(f"[1]当前已完成任务: {current_task}")
- # 按压力排序
- sorted_pressure = sorted(
- [(0, current_task[0]), (1, current_task[1]), (2, current_task[2])],
- key=lambda x: (-x[1], x[0])
- )
- # 交换第一和第二个元素的位置
- sorted_pressure[0], sorted_pressure[1] = sorted_pressure[1], sorted_pressure[0]
- print(f"[2]任务排序: {sorted_pressure}")
- print(f"[3]当前选择:{sorted_pressure[0][0]+1}")
- location_type_dict = json.loads(LocationQueries.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}")
- # 更新任务完成数目
- current_task[layer] = current_task[layer] + 1
- LocationUpdates.update_current_finish_task(container_code,current_task)
- return location_list[0]
-
- @transaction.atomic
- def get_location_group_remainder(location_group_code):
- """
- 获取可用库位的c_number列表
- :param location_group: 库位组
- :return: 可用库位编号列表
- """
- location_group = LocationGroupModel.objects.filter(
- group_code = location_group_code,
- ).first()
- if not location_group:
- return None
- 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")
-
- # 提取所有库位的 c_number
- c_numbers = [loc.c_number for loc in location_list]
- print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
- return location_list[0]
|