models.py 13 KB

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