2025-07-14 10:29:37 +08:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
库区分层数据模型
|
|
|
|
|
管理每个动作点的分层信息和货物状态
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import Column, String, Integer, Boolean, Text, DateTime, ForeignKey, UniqueConstraint
|
|
|
|
|
from sqlalchemy.orm import relationship
|
|
|
|
|
from sqlalchemy.dialects.mysql import CHAR
|
|
|
|
|
from .base import BaseModel
|
|
|
|
|
import datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OperatePointLayer(BaseModel):
|
|
|
|
|
"""
|
|
|
|
|
库区分层数据模型
|
|
|
|
|
每个动作点可以有多个层,每层可以独立存放货物
|
|
|
|
|
"""
|
|
|
|
|
__tablename__ = 'vwed_operate_point_layer'
|
|
|
|
|
|
|
|
|
|
id = Column(CHAR(64), primary_key=True, comment='层ID')
|
|
|
|
|
operate_point_id = Column(CHAR(64), ForeignKey('vwed_operate_point.id'), nullable=False, comment='动作点ID')
|
|
|
|
|
station_name = Column(String(64), nullable=False, comment='动作点名称')
|
|
|
|
|
storage_location_name = Column(String(64), nullable=False, comment='库位名称')
|
|
|
|
|
area_name = Column(String(64), nullable=True, comment='库区名称')
|
|
|
|
|
scene_id = Column(String(64), nullable=False, comment='场景ID(冗余字段)')
|
|
|
|
|
layer_index = Column(Integer, nullable=False, comment='层索引(从1开始)')
|
|
|
|
|
layer_name = Column(String(64), comment='层名称')
|
|
|
|
|
|
|
|
|
|
# 货物状态
|
|
|
|
|
is_occupied = Column(Boolean, nullable=False, default=False, comment='是否占用')
|
|
|
|
|
goods_content = Column(String(100), nullable=False, default='', comment='货物内容')
|
|
|
|
|
goods_weight = Column(Integer, comment='货物重量(克)')
|
|
|
|
|
goods_volume = Column(Integer, comment='货物体积(立方厘米)')
|
|
|
|
|
|
|
|
|
|
# 层状态
|
|
|
|
|
is_locked = Column(Boolean, nullable=False, default=False, comment='是否锁定')
|
|
|
|
|
is_disabled = Column(Boolean, nullable=False, default=False, comment='是否禁用')
|
|
|
|
|
is_empty_tray = Column(Boolean, nullable=False, default=False, comment='是否空托盘')
|
|
|
|
|
locked_by = Column(String(128), nullable=True, comment='锁定者')
|
|
|
|
|
tags = Column(String(100), nullable=False, default='', comment='标签')
|
|
|
|
|
|
|
|
|
|
# 层属性
|
|
|
|
|
max_weight = Column(Integer, comment='最大承重(克)')
|
|
|
|
|
max_volume = Column(Integer, comment='最大体积(立方厘米)')
|
|
|
|
|
layer_height = Column(Integer, comment='层高(毫米)')
|
|
|
|
|
|
|
|
|
|
# 时间信息
|
|
|
|
|
goods_stored_at = Column(DateTime, comment='货物存放时间')
|
|
|
|
|
goods_retrieved_at = Column(DateTime, comment='货物取出时间')
|
|
|
|
|
last_access_at = Column(DateTime, comment='最后访问时间')
|
|
|
|
|
|
|
|
|
|
# 扩展信息
|
|
|
|
|
tags = Column(String(255), comment='层标签', nullable=True)
|
|
|
|
|
description = Column(Text, comment='层描述', nullable=True)
|
|
|
|
|
config_json = Column(Text, comment='层配置JSON', nullable=True)
|
|
|
|
|
|
|
|
|
|
# 关联关系
|
|
|
|
|
operate_point = relationship("OperatePoint", back_populates="layers")
|
|
|
|
|
|
|
|
|
|
# 唯一约束:同一个动作点的层索引不能重复
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
UniqueConstraint('operate_point_id', 'layer_index', name='uk_operate_point_layer'),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
2025-07-15 15:05:04 +08:00
|
|
|
|
return f"<OperatePointLayer(id={self.id}, operate_point_id={self.operate_point_id}, station_name={self.station_name}, storage_location_name={self.storage_location_name}, area_name={self.area_name}, scene_id={self.scene_id}, layer_index={self.layer_index})>"
|
2025-07-14 10:29:37 +08:00
|
|
|
|
|
|
|
|
|
def can_store_goods(self, weight=None, volume=None):
|
|
|
|
|
"""
|
|
|
|
|
检查是否可以存放货物
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
weight: 货物重量(克)
|
|
|
|
|
volume: 货物体积(立方厘米)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 是否可以存放
|
|
|
|
|
"""
|
|
|
|
|
if self.is_disabled or self.is_locked or self.is_occupied:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 检查重量限制
|
|
|
|
|
if weight is not None and self.max_weight is not None:
|
|
|
|
|
if weight > self.max_weight:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 检查体积限制
|
|
|
|
|
if volume is not None and self.max_volume is not None:
|
|
|
|
|
if volume > self.max_volume:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def can_retrieve_goods(self):
|
|
|
|
|
"""检查是否可以取货"""
|
|
|
|
|
return (not self.is_disabled and
|
|
|
|
|
not self.is_locked and
|
|
|
|
|
self.is_occupied)
|
|
|
|
|
|
|
|
|
|
def store_goods(self, content, weight=None, volume=None):
|
|
|
|
|
"""
|
|
|
|
|
存放货物
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
content: 货物内容
|
|
|
|
|
weight: 货物重量(克)
|
|
|
|
|
volume: 货物体积(立方厘米)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: 是否成功
|
|
|
|
|
"""
|
|
|
|
|
if not self.can_store_goods(weight, volume):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
self.is_occupied = True
|
|
|
|
|
self.goods_content = content
|
|
|
|
|
self.goods_weight = weight
|
|
|
|
|
self.goods_volume = volume
|
|
|
|
|
self.goods_stored_at = datetime.datetime.now()
|
|
|
|
|
self.last_access_at = datetime.datetime.now()
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def retrieve_goods(self):
|
|
|
|
|
"""
|
|
|
|
|
取出货物
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
dict: 货物信息
|
|
|
|
|
"""
|
|
|
|
|
if not self.can_retrieve_goods():
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
goods_info = {
|
|
|
|
|
'content': self.goods_content,
|
|
|
|
|
'weight': self.goods_weight,
|
|
|
|
|
'volume': self.goods_volume,
|
|
|
|
|
'stored_at': self.goods_stored_at
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 清空货物信息
|
|
|
|
|
self.is_occupied = False
|
|
|
|
|
self.goods_content = ''
|
|
|
|
|
self.goods_weight = None
|
|
|
|
|
self.goods_volume = None
|
|
|
|
|
self.goods_retrieved_at = datetime.datetime.now()
|
|
|
|
|
self.last_access_at = datetime.datetime.now()
|
|
|
|
|
|
|
|
|
|
return goods_info
|
|
|
|
|
|
|
|
|
|
def get_remaining_capacity(self):
|
|
|
|
|
"""获取剩余容量信息"""
|
|
|
|
|
result = {}
|
|
|
|
|
|
|
|
|
|
if self.max_weight is not None:
|
|
|
|
|
used_weight = self.goods_weight if self.goods_weight is not None else 0
|
|
|
|
|
result['remaining_weight'] = self.max_weight - used_weight
|
|
|
|
|
|
|
|
|
|
if self.max_volume is not None:
|
|
|
|
|
used_volume = self.goods_volume if self.goods_volume is not None else 0
|
|
|
|
|
result['remaining_volume'] = self.max_volume - used_volume
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def is_overweight(self):
|
|
|
|
|
"""检查是否超重"""
|
|
|
|
|
if self.max_weight is None or self.goods_weight is None:
|
|
|
|
|
return False
|
|
|
|
|
return self.goods_weight > self.max_weight
|
|
|
|
|
|
|
|
|
|
def is_overflow(self):
|
|
|
|
|
"""检查是否超体积"""
|
|
|
|
|
if self.max_volume is None or self.goods_volume is None:
|
|
|
|
|
return False
|
|
|
|
|
return self.goods_volume > self.max_volume
|