models.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. from django.db import models
  2. from container.models import ContainerListModel,ContainerWCSModel,TaskModel
  3. # 库位主表(记录实时状态)
  4. class LocationModel(models.Model):
  5. LOCATION_TYPES = (
  6. ('T5', '5货位'), # 5托盘/批次
  7. ('T4', '4货位'), # 4托盘/批次
  8. ('S4', '4单货位'), # 4单托盘
  9. ('T2', '2货位'), # 2托盘/批次
  10. ('T1', '散货位'), # 1托盘
  11. ('M1', '通道区'), # 通道区
  12. ('E1', '提升机'), # 提升机
  13. ('C1', '输送机'), # 输送机
  14. )
  15. LOCATION_STATUS = (
  16. ('available', '可用'),
  17. ('occupied', '占用'),
  18. ('disabled', '禁用'),
  19. ('reserved', '预留'),
  20. ('maintenance', '维护中'),
  21. )
  22. warehouse_code = models.CharField(max_length=255, verbose_name="Warehouse code")
  23. warehouse_name = models.CharField(max_length=255, verbose_name="Warehouse Name")
  24. shelf_type = models.CharField(max_length=255,default = 'storage', verbose_name="Shelf Type")
  25. row = models.IntegerField(verbose_name="Row") # 位置的行号
  26. col = models.IntegerField(verbose_name="Column") # 位置的列号
  27. layer = models.IntegerField(verbose_name="Layer") # 位置的层号
  28. update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
  29. empty_label = models.BooleanField(default=True, verbose_name="Empty Flag")
  30. location_code = models.CharField(max_length=20, unique=True, verbose_name='库位编码')
  31. # 示例:T1-L1C001-1
  32. # T1-L1C001-1 代表货位类型为T1,层号为1,列号为001,货位号为1
  33. location_type = models.CharField(max_length=3, choices=LOCATION_TYPES, verbose_name='货位类型')
  34. status = models.CharField(max_length=20, choices=LOCATION_STATUS, default='available', verbose_name='库位状态')
  35. max_capacity = models.PositiveIntegerField(verbose_name='最大容量') # 根据类型自动设置
  36. current_quantity = models.PositiveIntegerField(default=0, verbose_name='当前托盘数')
  37. current_containers = models.ManyToManyField(ContainerListModel,
  38. through='LocationContainerLink',
  39. verbose_name='当前存放托盘')
  40. coordinate = models.CharField(max_length=50, verbose_name='三维坐标') # 格式:行-列-层
  41. access_priority = models.IntegerField(
  42. default=0,
  43. verbose_name='访问优先级',
  44. help_text='值越大表示越远离主通道,应优先使用'
  45. )
  46. is_active = models.BooleanField(default=True, verbose_name='是否有效')
  47. class Meta:
  48. db_table = 'location'
  49. verbose_name = 'Location'
  50. verbose_name_plural = "Location"
  51. ordering = ['-id']
  52. unique_together = ( 'warehouse_code','row', 'col', 'layer') # 防止重复坐标
  53. @classmethod
  54. def get_existing_positions(cls, warehouse_code, rows, cols, layers):
  55. """获取已存在的坐标集合"""
  56. return set(
  57. cls.objects.filter(
  58. warehouse_code=warehouse_code,
  59. row__lte=rows,
  60. col__lte=cols,
  61. layer__lte=layers
  62. ).values_list('row', 'col', 'layer')
  63. )
  64. # 在LocationModel类中新增方法
  65. @classmethod
  66. def generate_locations(cls, warehouse_code):
  67. # 清空现有数据(新增部分)
  68. cls.objects.filter(warehouse_code=warehouse_code).delete()
  69. """根据规划图生成库位"""
  70. # 定义核心参数(根据规划图调整)
  71. MAIN_AISLES = [18, 29] # 子通道列号
  72. SUB_AISLES = [2,8, 13] # 主通道行号
  73. for row in range(1, 16): # 1-15行
  74. for col in range(1, 19): # 1-19列
  75. for layer in range(1, 4): # 1-3层
  76. # 判断通道区
  77. if col in MAIN_AISLES or row in SUB_AISLES:
  78. loc_type = 'M1'
  79. c_number = row
  80. if col ==18 and row == 1:
  81. loc_type = 'T1'
  82. c_number = 1
  83. # 判断货位类型(根据实际规划)
  84. else:
  85. if row <2:
  86. loc_type = 'T1'
  87. c_number = 1
  88. elif row < 8:
  89. loc_type = 'T5'
  90. c_number = row-2
  91. elif row < 13:
  92. loc_type = 'T4'
  93. c_number = row-8
  94. else:
  95. loc_type = 'T2'
  96. c_number = 16-row
  97. # 生成唯一编码
  98. location_code = f"{loc_type}-L{layer}C{col:03d}-{c_number:02d}"
  99. print(f"生成库位:{location_code}-{row}-{col}-{layer}")
  100. # 创建库位
  101. cls.objects.update_or_create(
  102. warehouse_code=warehouse_code,
  103. row=row,
  104. col=col,
  105. layer=layer,
  106. defaults={
  107. 'location_code': location_code,
  108. 'location_type': loc_type,
  109. 'max_capacity': {
  110. 'T5': 5,
  111. 'T4': 4,
  112. 'T2': 2,
  113. 'T1': 1,
  114. 'M1': 0,
  115. 'E1': 0,
  116. 'C1': 0
  117. }[loc_type],
  118. 'coordinate': f"{row}-{col}-{layer}",
  119. 'is_active': True
  120. },
  121. access_priority=c_number if loc_type == 'T1'or loc_type == 'T2'or loc_type == 'T4'or loc_type == 'T5' else 0
  122. )
  123. print("✅ 库位生成成功!")
  124. for row in range(1, 18): # 1-17行
  125. for col in range(19, 30): # 19-29列
  126. for layer in range(1, 4): # 1-3层
  127. # 判断货位类型(根据实际规划)
  128. if row < 2:
  129. loc_type = 'T1'
  130. c_number = row
  131. if col ==18 and row == 1:
  132. loc_type = 'T1'
  133. c_number = 1
  134. if col ==29 and row == 1:
  135. loc_type = 'T1'
  136. c_number = 1
  137. elif row < 8:
  138. loc_type = 'T5'
  139. c_number = row-2
  140. elif row < 13:
  141. loc_type = 'T4'
  142. c_number = row-8
  143. else:
  144. loc_type = 'S4'
  145. c_number = 20-row
  146. # 生成唯一编码
  147. location_code = f"{loc_type}-L{layer}C{col:03d}-{c_number:02d}"
  148. # 生成唯一编码
  149. location_code = f"{loc_type}-L{layer}C{col:03d}-{c_number:02d}"
  150. print(f"生成库位:{location_code}-{row}-{col}-{layer}")
  151. # 创建库位
  152. cls.objects.update_or_create(
  153. warehouse_code=warehouse_code,
  154. row=row,
  155. col=col,
  156. layer=layer,
  157. defaults={
  158. 'location_code': location_code,
  159. 'location_type': loc_type,
  160. 'max_capacity': {
  161. 'T5': 5,
  162. 'T4': 4,
  163. 'S4': 4,
  164. 'T2': 2,
  165. 'T1': 1,
  166. 'M1': 0,
  167. 'E1': 0,
  168. 'C1': 0
  169. }[loc_type],
  170. 'coordinate': f"{row}-{col}-{layer}",
  171. 'is_active': True
  172. },
  173. access_priority=c_number if loc_type == 'T1'or loc_type == 'T2'or loc_type == 'T4'or loc_type == 'T5' else 0
  174. )
  175. # 库位-托盘关联表(记录实时存放关系)
  176. class LocationContainerLink(models.Model):
  177. location = models.ForeignKey(LocationModel, on_delete=models.CASCADE)
  178. container = models.ForeignKey(ContainerListModel, on_delete=models.CASCADE)
  179. task_wcs = models.ForeignKey(ContainerWCSModel, on_delete=models.CASCADE, null=True, blank=True)
  180. task_detail = models.ForeignKey(TaskModel, on_delete=models.CASCADE, null=True, blank=True)
  181. put_time = models.DateTimeField(auto_now_add=True, verbose_name='上架时间')
  182. operator = models.CharField(max_length=50, verbose_name='操作人')
  183. is_active = models.BooleanField(default=True, verbose_name='是否有效')
  184. class Meta:
  185. db_table = 'location_container_link'
  186. verbose_name = 'Location-Container Link'
  187. verbose_name_plural = "Location-Container Link"
  188. ordering = ['-id']
  189. unique_together = ( 'location', 'container') # 防止重复关联关系
  190. class DeviceModel(models.Model):
  191. location = models.ForeignKey(LocationModel, on_delete=models.CASCADE)
  192. device_id = models.CharField(max_length=255, verbose_name="Device ID")
  193. device_name = models.CharField(max_length=255, verbose_name="Device Name")
  194. device_type = models.CharField(max_length=255, verbose_name="Device Type")
  195. ip_address = models.CharField(max_length=255, verbose_name="IP Address")
  196. port = models.IntegerField(verbose_name="Port")
  197. status = models.CharField(max_length=255, verbose_name="Status")
  198. create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
  199. update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
  200. class Meta:
  201. db_table = 'device'
  202. verbose_name = 'Device'
  203. verbose_name_plural = "Device"
  204. ordering = ['-id']
  205. unique_together = ('device_id',) # 防止重复设备ID
  206. # 库位变更记录(历史追溯)
  207. class LocationChangeLog(models.Model):
  208. OPERATION_TYPES = (
  209. ('put', '上架'),
  210. ('pick', '下架'),
  211. ('move_in', '移入'),
  212. ('move_out', '移出')
  213. )
  214. location = models.ForeignKey(LocationModel, on_delete=models.CASCADE, verbose_name='库位')
  215. container = models.ForeignKey(ContainerListModel, on_delete=models.CASCADE, verbose_name='托盘')
  216. task_wcs = models.ForeignKey(ContainerWCSModel, on_delete=models.CASCADE, null=True, blank=True, verbose_name='WCS任务')
  217. task_detail = models.ForeignKey(TaskModel, on_delete=models.CASCADE, null=True, blank=True, verbose_name='批次详情')
  218. operation_type = models.CharField(max_length=10, choices=OPERATION_TYPES, verbose_name='操作类型')
  219. related_location = models.ForeignKey(LocationModel,
  220. on_delete=models.SET_NULL,
  221. null=True,
  222. related_name='related_logs',
  223. verbose_name='关联库位') # 用于移库操作
  224. timestamp = models.DateTimeField(auto_now_add=True, verbose_name='操作时间')
  225. class Meta:
  226. db_table = 'location_change_log'
  227. verbose_name = 'Location Change Log'
  228. verbose_name_plural = "Location Change Log"
  229. ordering = ['-id']