services.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. from django.db import transaction
  2. from .queries import LocationQueries
  3. from .updates import LocationUpdates
  4. from .algorithms import AllocationAlgorithm
  5. import logging
  6. import json
  7. from collections import defaultdict
  8. from .models import *
  9. from bound.models import BoundBatchModel, BoundDetailModel, OutBatchModel, BoundListModel
  10. import datetime
  11. from django.utils import timezone
  12. logger = logging.getLogger(__name__)
  13. class AllocationService:
  14. @classmethod
  15. @transaction.atomic
  16. def allocate(cls, container_code, start_point):
  17. logger.info(f"请求托盘:{container_code},请求位置:{start_point}")
  18. batch_info = LocationQueries.get_batch_info(container_code)
  19. if not batch_info :
  20. raise ValueError("无效的容器或批次信息")
  21. if not batch_info['number']:
  22. if batch_info['class'] == 2:
  23. return cls._container_group_allocation(container_code,batch_info, start_point)
  24. else:
  25. return cls._container_scattered_allocation(container_code,batch_info, start_point)
  26. if batch_info['status'] == 1 or batch_info['status'] == 0:
  27. return cls._first_allocation(container_code, batch_info, start_point)
  28. elif batch_info['status'] == 2:
  29. return cls._subsequent_allocation(container_code, batch_info, start_point)
  30. else:
  31. raise ValueError("非法的批次状态")
  32. @classmethod
  33. @transaction.atomic
  34. def _first_allocation(cls, container_code, batch_info, start_point):
  35. total = LocationQueries.get_pallet_count_by_batch(container_code)
  36. current_total = LocationQueries.get_pallet_count(container_code)
  37. if not total:
  38. raise ValueError("无效的容器或批次信息")
  39. LocationUpdates.update_pallet_count(total, batch_info['number'])
  40. if current_total == 0:
  41. layer_cap = LocationQueries.get_group_capacity()
  42. pressure = LocationQueries.get_current_pressure()
  43. solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
  44. if not solution:
  45. raise RuntimeError("无法生成有效分配方案")
  46. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  47. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  48. # 使用距离最近的库位,增加切换
  49. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
  50. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  51. else:
  52. locations = LocationQueries.get_left_location_group(batch_info['number'])
  53. if not locations:
  54. layer_cap = LocationQueries.get_group_capacity()
  55. pressure = LocationQueries.get_current_pressure()
  56. finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
  57. solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum, layer_cap, pressure)
  58. if not solution:
  59. raise RuntimeError("无法生成有效分配方案")
  60. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  61. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  62. # 使用距离最近的库位,增加切换
  63. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
  64. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  65. else:
  66. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  67. @classmethod
  68. def _container_group_allocation(cls,container_code, batch_info, start_point):
  69. total = 1
  70. batch_info['number'] = 'ContainerGroup'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
  71. if not total:
  72. raise ValueError("无效的容器或批次信息")
  73. layer_cap = LocationQueries.get_group_capacity()
  74. pressure = LocationQueries.get_current_pressure()
  75. solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
  76. if not solution:
  77. raise RuntimeError("无法生成有效分配方案")
  78. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  79. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  80. # 使用距离最近的库位,增加切换
  81. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
  82. return cls._execute_allocation_no_batch(locations, start_point,container_code,batch_info)
  83. @classmethod
  84. def _container_scattered_allocation(cls,container_code, batch_info, start_point):
  85. total = 1
  86. batch_info['number'] = 'ContainerScattered'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
  87. layer_cap = LocationQueries.get_group_capacity()
  88. pressure = LocationQueries.get_current_pressure()
  89. solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
  90. if not solution:
  91. raise RuntimeError("无法生成有效分配方案")
  92. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  93. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  94. # 使用距离最近的库位,增加切换
  95. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
  96. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  97. @classmethod
  98. def _execute_allocation_no_batch(cls, solution, start_point,container_code,batch_info):
  99. LocationUpdates.update_group_status_reserved(solution)
  100. print(f"[0] 库位组状态预定成功!")
  101. location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
  102. if not location_min_value:
  103. raise ValueError("[1] 任务分配失败,无可用库位")
  104. print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
  105. location_min_value.status = 'reserved'
  106. location_min_value.save()
  107. print(f"[2] 库位状态更新成功!")
  108. update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
  109. if not update_location_group_status:
  110. raise ValueError("[3] 库位组状态更新失败")
  111. print(f"[3] 库位组状态更新成功!")
  112. update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
  113. if not update_location_group_batch:
  114. raise ValueError("[5] 批次信息更新失败")
  115. print(f"[5] 批次信息更新成功!")
  116. update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
  117. if not update_location_container_link:
  118. raise ValueError("[6] 容器与库位关联失败")
  119. print(f"[6] 容器与库位关联成功!")
  120. update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
  121. if not update_location_container_detail:
  122. raise ValueError("[7] 托盘详情状态更新失败")
  123. print(f"[7] 托盘详情状态更新成功!")
  124. allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
  125. return location_min_value,allocation_target_location, batch_info
  126. @classmethod
  127. def _execute_allocation(cls, solution, start_point,container_code,batch_info):
  128. LocationUpdates.update_group_status_reserved(solution)
  129. print(f"[0] 库位组状态预定成功!")
  130. location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
  131. if not location_min_value:
  132. raise ValueError("[1] 任务分配失败,无可用库位")
  133. print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
  134. location_min_value.status = 'reserved'
  135. location_min_value.save()
  136. print(f"[2] 库位状态更新成功!")
  137. update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
  138. if not update_location_group_status:
  139. raise ValueError("[3] 库位组状态更新失败")
  140. print(f"[3] 库位组状态更新成功!")
  141. update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
  142. if not update_batch_status:
  143. raise ValueError("[4] 批次状态更新失败")
  144. print(f"[4] 批次状态更新成功!")
  145. update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
  146. if not update_location_group_batch:
  147. raise ValueError("[5] 批次信息更新失败")
  148. print(f"[5] 批次信息更新成功!")
  149. update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
  150. if not update_location_container_link:
  151. raise ValueError("[6] 容器与库位关联失败")
  152. print(f"[6] 容器与库位关联成功!")
  153. update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
  154. if not update_location_container_detail:
  155. raise ValueError("[7] 托盘详情状态更新失败")
  156. print(f"[7] 托盘详情状态更新成功!")
  157. allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
  158. return location_min_value,allocation_target_location, batch_info
  159. @classmethod
  160. def _subsequent_allocation(cls, container_code, batch_info, start_point):
  161. # 后续分配逻辑
  162. total = LocationQueries.get_pallet_count_by_batch(container_code)
  163. current_total = LocationQueries.get_pallet_count(container_code)
  164. LocationUpdates.update_pallet_count(total, batch_info['number'])
  165. if total == current_total:
  166. locations = LocationQueries.get_left_location_group(batch_info['number'])
  167. if not locations:
  168. layer_cap = LocationQueries.get_group_capacity()
  169. pressure = LocationQueries.get_current_pressure()
  170. finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
  171. if finish_task_sum == total:
  172. total += 1
  173. solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum, layer_cap, pressure)
  174. if not solution:
  175. raise RuntimeError("无法生成有效分配方案")
  176. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  177. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  178. # 使用距离最近的库位,增加切换
  179. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
  180. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  181. else:
  182. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  183. @transaction.atomic
  184. def get_location_list_remainder(location_group_list,container_code,batch_info):
  185. """
  186. 获取可用库位的c_number列表
  187. :param location_list: 库位对象列表
  188. :return: 可用库位编号列表
  189. """
  190. if not location_group_list:
  191. return None
  192. current_task = LocationQueries.get_current_finish_task(container_code,batch_info)
  193. print(f"[1]当前已完成任务: {current_task}")
  194. # 按压力排序
  195. sorted_pressure = sorted(
  196. [(0, current_task[0]), (1, current_task[1]), (2, current_task[2])],
  197. key=lambda x: (-x[1], x[0])
  198. )
  199. # 交换第一和第二个元素的位置
  200. sorted_pressure[0], sorted_pressure[1] = sorted_pressure[1], sorted_pressure[0]
  201. print(f"[2]任务排序: {sorted_pressure}")
  202. print(f"[3]当前选择:{sorted_pressure[0][0]+1}")
  203. location_type_dict = json.loads(LocationQueries.divide_solution_by_layer(location_group_list))
  204. # print(f"库位类型分配方案: {location_type_dict}")
  205. for layer, _ in sorted_pressure:
  206. if not location_type_dict.get(str(layer+1)):
  207. continue
  208. # print(f"当前层: {layer+1}")
  209. # print(f"当前层库位组: {location_type_dict[str(layer+1)].keys()}")
  210. for group_id in location_type_dict[str(layer+1)].keys():
  211. location_group = LocationGroupModel.objects.filter(
  212. id=group_id,
  213. ).first()
  214. if not location_group:
  215. continue
  216. location_list = location_group.location_items.filter(
  217. status='available'
  218. ).all().order_by('c_number')
  219. if not location_list:
  220. print(f"当前层库位组 {location_group.group_code} 可用库位: None")
  221. continue
  222. # 提取所有库位的 c_number
  223. c_numbers = [loc.c_number for loc in location_list]
  224. print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
  225. # 更新任务完成数目
  226. current_task[layer] = current_task[layer] + 1
  227. LocationUpdates.update_current_finish_task(container_code,current_task)
  228. return location_list[0]