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']: return cls._container_group_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 = 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, 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: 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 _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 _subsequent_allocation(cls, container_code, batch_info, start_point): # 后续分配逻辑 total = LocationQueries.get_pallet_count_by_batch(container_code) current_total = LocationQueries.get_pallet_count(container_code) LocationUpdates.update_pallet_count(total, batch_info['number']) if total == current_total: 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) @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]