services.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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 , scatter_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-scatter_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. return cls._subsequent_allocation(container_code, batch_info, start_point)
  53. # locations = LocationQueries.get_left_location_group(batch_info['number'])
  54. # if not locations:
  55. # layer_cap = LocationQueries.get_group_capacity()
  56. # pressure = LocationQueries.get_current_pressure()
  57. # finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
  58. # solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum, layer_cap, pressure)
  59. # if not solution:
  60. # raise RuntimeError("无法生成有效分配方案")
  61. # formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  62. # LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  63. # # 使用距离最近的库位,增加切换
  64. # locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
  65. # return cls._execute_allocation(locations, start_point,container_code,batch_info)
  66. # else:
  67. # return cls._execute_allocation(locations, start_point,container_code,batch_info)
  68. @classmethod
  69. def _container_group_allocation(cls,container_code, batch_info, start_point):
  70. total = 1
  71. batch_info['number'] = 'ContainerGroup'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
  72. if not total:
  73. raise ValueError("无效的容器或批次信息")
  74. layer_cap = LocationQueries.get_group_capacity()
  75. pressure = LocationQueries.get_current_pressure()
  76. solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
  77. if not solution:
  78. raise RuntimeError("无法生成有效分配方案")
  79. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  80. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  81. # 使用距离最近的库位,增加切换
  82. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
  83. return cls._execute_allocation_no_batch(locations, start_point,container_code,batch_info)
  84. @classmethod
  85. def _container_scattered_allocation(cls,container_code, batch_info, start_point):
  86. total = 1
  87. batch_info['number'] = 'ContainerScattered'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
  88. layer_cap = LocationQueries.get_group_capacity()
  89. pressure = LocationQueries.get_current_pressure()
  90. solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
  91. if not solution:
  92. raise RuntimeError("无法生成有效分配方案")
  93. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  94. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  95. # 使用距离最近的库位,增加切换
  96. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
  97. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  98. @classmethod
  99. def _execute_allocation_no_batch(cls, solution, start_point,container_code,batch_info):
  100. LocationUpdates.update_group_status_reserved(solution)
  101. print(f"[0] 库位组状态预定成功!")
  102. location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
  103. if not location_min_value:
  104. raise ValueError("[1] 任务分配失败,无可用库位")
  105. print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
  106. location_min_value.status = 'reserved'
  107. location_min_value.save()
  108. print(f"[2] 库位状态更新成功!")
  109. update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
  110. if not update_location_group_status:
  111. raise ValueError("[3] 库位组状态更新失败")
  112. print(f"[3] 库位组状态更新成功!")
  113. update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
  114. if not update_location_group_batch:
  115. raise ValueError("[5] 批次信息更新失败")
  116. print(f"[5] 批次信息更新成功!")
  117. update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
  118. if not update_location_container_link:
  119. raise ValueError("[6] 容器与库位关联失败")
  120. print(f"[6] 容器与库位关联成功!")
  121. update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
  122. if not update_location_container_detail:
  123. raise ValueError("[7] 托盘详情状态更新失败")
  124. print(f"[7] 托盘详情状态更新成功!")
  125. allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
  126. return location_min_value,allocation_target_location, batch_info
  127. @classmethod
  128. def _execute_allocation(cls, solution, start_point,container_code,batch_info):
  129. LocationUpdates.update_group_status_reserved(solution)
  130. print(f"[0] 库位组状态预定成功!")
  131. location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
  132. if not location_min_value:
  133. raise ValueError("[1] 任务分配失败,无可用库位")
  134. print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
  135. location_min_value.status = 'reserved'
  136. location_min_value.save()
  137. print(f"[2] 库位状态更新成功!")
  138. update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
  139. if not update_location_group_status:
  140. raise ValueError("[3] 库位组状态更新失败")
  141. print(f"[3] 库位组状态更新成功!")
  142. update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
  143. if not update_batch_status:
  144. raise ValueError("[4] 批次状态更新失败")
  145. print(f"[4] 批次状态更新成功!")
  146. update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
  147. if not update_location_group_batch:
  148. raise ValueError("[5] 批次信息更新失败")
  149. print(f"[5] 批次信息更新成功!")
  150. update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
  151. if not update_location_container_link:
  152. raise ValueError("[6] 容器与库位关联失败")
  153. print(f"[6] 容器与库位关联成功!")
  154. update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
  155. if not update_location_container_detail:
  156. raise ValueError("[7] 托盘详情状态更新失败")
  157. print(f"[7] 托盘详情状态更新成功!")
  158. allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
  159. return location_min_value,allocation_target_location, batch_info
  160. @classmethod
  161. def _move_allocation(cls, start_point,target_point,container_code):
  162. batch_info = LocationQueries.get_batch_info(container_code)
  163. start_location = AllocationAlgorithm.generate_WMS_location(start_point)
  164. target_location = AllocationAlgorithm.generate_WMS_location(target_point)
  165. if not start_location or not target_location:
  166. raise ValueError("无效的起始或目标位置")
  167. if start_location.location_code == target_location.location_code:
  168. raise ValueError("起始位置与目标位置相同")
  169. location_min_value = AllocationService.get_location_group_remainder(target_location.location_group)
  170. if not location_min_value:
  171. raise ValueError("[1] 任务分配失败,无可用库位")
  172. print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
  173. start_location.status = 'available'
  174. start_location.save()
  175. location_min_value.status = 'occupied'
  176. location_min_value.save()
  177. print(f"[2] 库位状态更新成功!")
  178. update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
  179. update_start_location_group_status= LocationUpdates.update_location_group_status(start_location.location_code)
  180. if not update_location_group_status or not update_start_location_group_status:
  181. raise ValueError("[3] 库位组状态更新失败")
  182. print(f"[3] 库位组状态更新成功!")
  183. # update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
  184. # if not update_batch_status:
  185. # raise ValueError("[4] 批次状态更新失败")
  186. # print(f"[4] 批次状态更新成功!")
  187. update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
  188. if not update_location_group_batch:
  189. raise ValueError("[5] 批次信息更新失败")
  190. print(f"[5] 批次信息更新成功!")
  191. update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
  192. if not update_location_container_link:
  193. raise ValueError("[6] 容器与库位关联失败")
  194. print(f"[6] 容器与库位关联成功!")
  195. update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
  196. if not update_location_container_detail:
  197. raise ValueError("[7] 托盘详情状态更新失败")
  198. print(f"[7] 托盘详情状态更新成功!")
  199. allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
  200. return location_min_value,allocation_target_location, batch_info
  201. @classmethod
  202. def _subsequent_allocation(cls, container_code, batch_info, start_point):
  203. total , scatter_total = LocationQueries.get_pallet_count_by_batch(container_code)
  204. LocationUpdates.update_pallet_count(total, batch_info['number'])
  205. locations = LocationQueries.get_left_location_group(batch_info['number'])
  206. if not locations:
  207. layer_cap = LocationQueries.get_group_capacity()
  208. pressure = LocationQueries.get_current_pressure()
  209. finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info))
  210. print(f"当前已完成任务: {finish_task_sum}, 总任务数: {total},散盘任务数: {scatter_total}")
  211. if total-finish_task_sum-scatter_total == 0:
  212. total += 1
  213. solution, new_pressure = AllocationAlgorithm.generate_plan(total-finish_task_sum-scatter_total, layer_cap, pressure)
  214. if not solution:
  215. raise RuntimeError("无法生成有效分配方案")
  216. formatted = json.loads(AllocationAlgorithm.format_solution(solution))
  217. LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
  218. # 使用距离最近的库位,增加切换
  219. locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
  220. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  221. else:
  222. return cls._execute_allocation(locations, start_point,container_code,batch_info)
  223. @transaction.atomic
  224. def get_location_list_remainder(location_group_list,container_code,batch_info):
  225. """
  226. 获取可用库位的c_number列表
  227. :param location_list: 库位对象列表
  228. :return: 可用库位编号列表
  229. """
  230. if not location_group_list:
  231. return None
  232. current_task = LocationQueries.get_current_finish_task(container_code,batch_info)
  233. print(f"[1]当前已完成任务: {current_task}")
  234. # 按压力排序
  235. sorted_pressure = sorted(
  236. [(0, current_task[0]), (1, current_task[1]), (2, current_task[2])],
  237. key=lambda x: (-x[1], x[0])
  238. )
  239. # 交换第一和第二个元素的位置
  240. sorted_pressure[0], sorted_pressure[1] = sorted_pressure[1], sorted_pressure[0]
  241. print(f"[2]任务排序: {sorted_pressure}")
  242. print(f"[3]当前选择:{sorted_pressure[0][0]+1}")
  243. location_type_dict = json.loads(LocationQueries.divide_solution_by_layer(location_group_list))
  244. # print(f"库位类型分配方案: {location_type_dict}")
  245. for layer, _ in sorted_pressure:
  246. if not location_type_dict.get(str(layer+1)):
  247. continue
  248. # print(f"当前层: {layer+1}")
  249. # print(f"当前层库位组: {location_type_dict[str(layer+1)].keys()}")
  250. for group_id in location_type_dict[str(layer+1)].keys():
  251. location_group = LocationGroupModel.objects.filter(
  252. id=group_id,
  253. ).first()
  254. if not location_group:
  255. continue
  256. location_list = location_group.location_items.filter(
  257. status='available'
  258. ).all().order_by('c_number')
  259. if not location_list:
  260. print(f"当前层库位组 {location_group.group_code} 可用库位: None")
  261. continue
  262. # 提取所有库位的 c_number
  263. c_numbers = [loc.c_number for loc in location_list]
  264. print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
  265. # 更新任务完成数目
  266. current_task[layer] = current_task[layer] + 1
  267. LocationUpdates.update_current_finish_task(container_code,current_task)
  268. return location_list[0]
  269. @transaction.atomic
  270. def get_location_group_remainder(location_group_code):
  271. """
  272. 获取可用库位的c_number列表
  273. :param location_group: 库位组
  274. :return: 可用库位编号列表
  275. """
  276. location_group = LocationGroupModel.objects.filter(
  277. group_code = location_group_code,
  278. ).first()
  279. if not location_group:
  280. return None
  281. location_list = location_group.location_items.filter(
  282. status='available'
  283. ).all().order_by('c_number')
  284. if not location_list:
  285. print(f"当前层库位组 {location_group.group_code} 可用库位: None")
  286. # 提取所有库位的 c_number
  287. c_numbers = [loc.c_number for loc in location_list]
  288. print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
  289. return location_list[0]