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]