|
|
@@ -1,4 +1,3 @@
|
|
|
-from wsgiref import headers
|
|
|
from rest_framework.viewsets import ViewSet
|
|
|
from rest_framework import viewsets
|
|
|
from utils.page import MyPageNumberPagination
|
|
|
@@ -48,6 +47,105 @@ loggertask = logging.getLogger('wms.WCSTask')
|
|
|
from .models import DispatchConfig
|
|
|
from .serializers import DispatchConfigSerializer
|
|
|
|
|
|
+DEFAULT_LOCATION_GROUP_ID = 0
|
|
|
+DEFAULT_ORDER_NUMBER = 0
|
|
|
+LOCATION_CODE_REGEX = re.compile(r'([A-Z0-9]+-L\d+C\d{3}-\d{2})', re.IGNORECASE)
|
|
|
+GROUP_CODE_REGEX = re.compile(r'([A-Z0-9]+-L\d+C\d{3})', re.IGNORECASE)
|
|
|
+COORDINATE_SUFFIX_REGEX = re.compile(r'(\d+)-(\d+)-(\d+)$')
|
|
|
+
|
|
|
+
|
|
|
+def select_reference_location(task_type, current_location, target_location):
|
|
|
+ """
|
|
|
+ 根据任务类型确定需要解析的库位:入库/移库看目标,其余看当前
|
|
|
+ """
|
|
|
+ normalized = str(task_type or '').lower()
|
|
|
+ if normalized in ('inbound', 'move', 'putaway'):
|
|
|
+ return target_location or current_location
|
|
|
+ return current_location or target_location
|
|
|
+
|
|
|
+
|
|
|
+def _find_location_instance(location_str):
|
|
|
+ if not location_str:
|
|
|
+ return None
|
|
|
+ normalized = str(location_str).strip()
|
|
|
+ if not normalized:
|
|
|
+ return None
|
|
|
+ candidates = {normalized, normalized.upper()}
|
|
|
+ regex_match = LOCATION_CODE_REGEX.search(normalized)
|
|
|
+ if regex_match:
|
|
|
+ candidates.add(regex_match.group(1).upper())
|
|
|
+ for candidate in candidates:
|
|
|
+ location = LocationModel.objects.filter(
|
|
|
+ location_code=candidate
|
|
|
+ ).only('id', 'location_group', 'c_number').first()
|
|
|
+ if location:
|
|
|
+ return location
|
|
|
+ coordinate_match = COORDINATE_SUFFIX_REGEX.search(normalized)
|
|
|
+ if coordinate_match:
|
|
|
+ row, col, layer = coordinate_match.groups()
|
|
|
+ try:
|
|
|
+ return LocationModel.objects.filter(
|
|
|
+ row=int(row),
|
|
|
+ col=int(col),
|
|
|
+ layer=int(layer)
|
|
|
+ ).only('id', 'location_group', 'c_number').first()
|
|
|
+ except ValueError:
|
|
|
+ return None
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+def _extract_group_code(location_str):
|
|
|
+ if not location_str:
|
|
|
+ return None
|
|
|
+ match = GROUP_CODE_REGEX.search(location_str)
|
|
|
+ if match:
|
|
|
+ return match.group(1).upper()
|
|
|
+ parts = str(location_str).split('-')
|
|
|
+ if len(parts) > 1 and parts[-1].isdigit():
|
|
|
+ candidate = '-'.join(parts[:-1])
|
|
|
+ if GROUP_CODE_REGEX.match(candidate):
|
|
|
+ return candidate.upper()
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+def resolve_location_group_metadata(location_value):
|
|
|
+ defaults = {
|
|
|
+ 'location_group_id': DEFAULT_LOCATION_GROUP_ID,
|
|
|
+ 'order_number': DEFAULT_ORDER_NUMBER,
|
|
|
+ }
|
|
|
+ if location_value is None:
|
|
|
+ return defaults.copy()
|
|
|
+ location_str = str(location_value).strip()
|
|
|
+ if not location_str:
|
|
|
+ return defaults.copy()
|
|
|
+ location_instance = _find_location_instance(location_str)
|
|
|
+ if location_instance:
|
|
|
+ group_code = location_instance.location_group
|
|
|
+ group = LocationGroupModel.objects.filter(
|
|
|
+ group_code=group_code
|
|
|
+ ).only('id').first()
|
|
|
+ return {
|
|
|
+ 'location_group_id': group.id if group else defaults['location_group_id'],
|
|
|
+ 'order_number': (
|
|
|
+ location_instance.c_number
|
|
|
+ ) or defaults['order_number'],
|
|
|
+ }
|
|
|
+ group_code = _extract_group_code(location_str)
|
|
|
+ if group_code:
|
|
|
+ group = LocationGroupModel.objects.filter(
|
|
|
+ group_code=group_code
|
|
|
+ ).only('id').first()
|
|
|
+ if group:
|
|
|
+ return {
|
|
|
+ 'location_group_id': group.id,
|
|
|
+ 'order_number': defaults['order_number'],
|
|
|
+ }
|
|
|
+ return defaults.copy()
|
|
|
+
|
|
|
+
|
|
|
+def get_task_location_metadata(task_type, current_location, target_location):
|
|
|
+ reference_location = select_reference_location(task_type, current_location, target_location)
|
|
|
+ return resolve_location_group_metadata(reference_location)
|
|
|
|
|
|
# 托盘分类视图
|
|
|
# 借助LocationContainerLink,其中
|
|
|
@@ -126,7 +224,7 @@ class batchLogModelViewSet(viewsets.ModelViewSet):
|
|
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
|
data = self.request.data
|
|
|
- return Response(data, status=200, headers=headers)
|
|
|
+ return Response(data, status=200)
|
|
|
|
|
|
def update(self, request, pk):
|
|
|
qs = self.get_object()
|
|
|
@@ -191,7 +289,7 @@ class ContainerDetailLogModelViewSet(viewsets.ModelViewSet):
|
|
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
|
data = self.request.data
|
|
|
- return Response(data, status=200, headers=headers)
|
|
|
+ return Response(data, status=200)
|
|
|
|
|
|
def update(self, request, pk):
|
|
|
qs = self.get_object()
|
|
|
@@ -354,10 +452,17 @@ class WCSTaskViewSet(viewsets.ModelViewSet):
|
|
|
def get_queryset(self):
|
|
|
id = self.get_project()
|
|
|
if self.request.user:
|
|
|
+ base_qs = ContainerWCSModel.objects.select_related(
|
|
|
+ 'batch',
|
|
|
+ 'batch_out',
|
|
|
+ 'batch_out__batch_number',
|
|
|
+ 'batch_out__bound_list',
|
|
|
+ 'bound_list'
|
|
|
+ )
|
|
|
if id is None:
|
|
|
- return ContainerWCSModel.objects.filter()
|
|
|
+ return base_qs
|
|
|
else:
|
|
|
- return ContainerWCSModel.objects.filter(id=id)
|
|
|
+ return base_qs.filter(id=id)
|
|
|
else:
|
|
|
return ContainerWCSModel.objects.none()
|
|
|
|
|
|
@@ -472,7 +577,7 @@ class TaskViewSet(viewsets.ModelViewSet):
|
|
|
def create(self, request, *args, **kwargs):
|
|
|
data = self.request.data
|
|
|
|
|
|
- return Response(data, status=200, headers=headers)
|
|
|
+ return Response(data, status=200)
|
|
|
|
|
|
def update(self, request, pk):
|
|
|
qs = self.get_object()
|
|
|
@@ -1137,6 +1242,7 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
|
|
|
'status': 103,
|
|
|
'is_delete': False
|
|
|
}
|
|
|
+ data_tosave.update(get_task_location_metadata(tasktype, current_location, target_location))
|
|
|
|
|
|
# 生成唯一递增的 taskid
|
|
|
last_task = ContainerWCSModel.objects.filter(
|
|
|
@@ -1175,6 +1281,7 @@ class ContainerWCSViewSet(viewsets.ModelViewSet):
|
|
|
'status': 103,
|
|
|
'is_delete': False
|
|
|
}
|
|
|
+ data_tosave.update(get_task_location_metadata(tasktype, current_location, target_location))
|
|
|
|
|
|
# 生成唯一递增的 taskid
|
|
|
last_task = ContainerWCSModel.objects.filter(
|
|
|
@@ -2148,6 +2255,22 @@ class OutboundService:
|
|
|
@staticmethod
|
|
|
def send_task_to_wcs(task):
|
|
|
"""异步发送任务到WCS(非阻塞版本)"""
|
|
|
+ reference_location = select_reference_location(
|
|
|
+ task.tasktype,
|
|
|
+ task.current_location,
|
|
|
+ task.target_location
|
|
|
+ )
|
|
|
+ if task.location_group_id is None or task.order_number in (None, 0):
|
|
|
+ location_meta = resolve_location_group_metadata(reference_location)
|
|
|
+ update_fields = []
|
|
|
+ if task.location_group_id is None and location_meta['location_group_id'] is not None:
|
|
|
+ task.location_group_id = location_meta['location_group_id']
|
|
|
+ update_fields.append('location_group_id')
|
|
|
+ if task.order_number in (None, 0) and location_meta['order_number'] not in (None, 0):
|
|
|
+ task.order_number = location_meta['order_number']
|
|
|
+ update_fields.append('order_number')
|
|
|
+ if update_fields:
|
|
|
+ task.save(update_fields=update_fields)
|
|
|
# 提取任务关键数据用于线程(避免直接传递ORM对象)
|
|
|
task_data = {
|
|
|
'task_id': task.pk, # 使用主键而不是对象
|
|
|
@@ -2165,7 +2288,8 @@ class OutboundService:
|
|
|
"status": task.status,
|
|
|
"taskNumber": task.tasknumber-20000000000,
|
|
|
"order_number":task.order_number,
|
|
|
- "sequence":task.sequence
|
|
|
+ "sequence":task.sequence,
|
|
|
+ "location_group_id": task.location_group_id or DEFAULT_LOCATION_GROUP_ID,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -2184,6 +2308,7 @@ class OutboundService:
|
|
|
'sequence': task.sequence,
|
|
|
'response_data': task_data,
|
|
|
'log_type': task.tasktype or 'outbound',
|
|
|
+ 'location_group_id': task.location_group_id or DEFAULT_LOCATION_GROUP_ID,
|
|
|
},
|
|
|
daemon=True
|
|
|
)
|
|
|
@@ -2197,37 +2322,83 @@ class OutboundService:
|
|
|
)
|
|
|
thread.start()
|
|
|
return True # 立即返回表示已开始处理
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def _extract_layer(location):
|
|
|
+ """解析库位字符串中的楼层信息"""
|
|
|
+ try:
|
|
|
+ parts = str(location).split('-')
|
|
|
+ return parts[3] if len(parts) >= 4 else None
|
|
|
+ except Exception:
|
|
|
+ return None
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def _get_running_layers():
|
|
|
+ """获取当前正在执行中的出库任务所在楼层"""
|
|
|
+ running_locations = ContainerWCSModel.objects.filter(
|
|
|
+ tasktype='outbound',
|
|
|
+ status__gt=100,
|
|
|
+ status__lt=300,
|
|
|
+ working=1,
|
|
|
+ is_delete=False
|
|
|
+ ).values_list('current_location', flat=True)
|
|
|
+ active_layers = set()
|
|
|
+ for location in running_locations:
|
|
|
+ layer = OutboundService._extract_layer(location)
|
|
|
+ if layer:
|
|
|
+ active_layers.add(layer)
|
|
|
+ return active_layers
|
|
|
|
|
|
@staticmethod
|
|
|
- def _async_log_handler(task_number, container, current_location, target_location, task_type, order_number, sequence, response_data, log_type='outbound'):
|
|
|
+ def _async_log_handler(task_number, container, current_location, target_location, task_type, order_number, sequence, response_data, log_type='outbound', location_group_id=None):
|
|
|
"""异步记录 WCS 任务发送日志到数据库(不阻塞发送)"""
|
|
|
try:
|
|
|
close_old_connections()
|
|
|
|
|
|
# 解析库位组与优先级
|
|
|
- group_id = None
|
|
|
- access_priority = None
|
|
|
+ group_id = location_group_id
|
|
|
+ order_number_value = order_number
|
|
|
left_priority = None
|
|
|
right_priority = None
|
|
|
floor = None
|
|
|
- try:
|
|
|
- parts = current_location.split('-')
|
|
|
- if len(parts) >= 4:
|
|
|
- row = int(parts[1])
|
|
|
- col = int(parts[2])
|
|
|
- layer = int(parts[3])
|
|
|
- floor = parts[3]
|
|
|
- loc = LocationModel.objects.filter(row=row, col=col, layer=layer).first()
|
|
|
- if loc:
|
|
|
- group_code = loc.location_group
|
|
|
- group = LocationGroupModel.objects.filter(group_code=group_code).first()
|
|
|
- if group:
|
|
|
- group_id = group.id
|
|
|
- access_priority = loc.c_number
|
|
|
- left_priority = group.left_priority
|
|
|
- right_priority = group.right_priority
|
|
|
- except Exception as e:
|
|
|
- logger.error(f"解析库位组信息失败: {e}")
|
|
|
+ group_obj = None
|
|
|
+ if group_id not in (None, 0):
|
|
|
+ group_obj = LocationGroupModel.objects.filter(
|
|
|
+ id=group_id
|
|
|
+ ).only('id', 'group_code', 'left_priority', 'right_priority').first()
|
|
|
+ if group_obj:
|
|
|
+ left_priority = group_obj.left_priority
|
|
|
+ right_priority = group_obj.right_priority
|
|
|
+ if group_obj is None or order_number_value in (None, 0):
|
|
|
+ try:
|
|
|
+ parts = current_location.split('-')
|
|
|
+ if len(parts) >= 4:
|
|
|
+ row = int(parts[1])
|
|
|
+ col = int(parts[2])
|
|
|
+ layer = int(parts[3])
|
|
|
+ floor = parts[3]
|
|
|
+ loc = LocationModel.objects.filter(row=row, col=col, layer=layer).only(
|
|
|
+ 'location_group', 'c_number'
|
|
|
+ ).first()
|
|
|
+ if loc:
|
|
|
+ group = group_obj or LocationGroupModel.objects.filter(
|
|
|
+ group_code=loc.location_group
|
|
|
+ ).first()
|
|
|
+ if group:
|
|
|
+ group_obj = group
|
|
|
+ group_id = group.id
|
|
|
+ left_priority = group.left_priority
|
|
|
+ right_priority = group.right_priority
|
|
|
+ if order_number_value in (None, 0):
|
|
|
+ order_number_value = loc.c_number or DEFAULT_ORDER_NUMBER
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"解析库位组信息失败: {e}")
|
|
|
+ if group_id in (None, 0) or order_number_value in (None, 0):
|
|
|
+ metadata = resolve_location_group_metadata(current_location)
|
|
|
+ if group_id in (None, 0):
|
|
|
+ group_id = metadata['location_group_id']
|
|
|
+ if order_number_value in (None, 0):
|
|
|
+ order_number_value = metadata.get('order_number', DEFAULT_ORDER_NUMBER)
|
|
|
|
|
|
# 创建日志记录
|
|
|
WCSTaskLogModel.objects.create(
|
|
|
@@ -2235,12 +2406,11 @@ class OutboundService:
|
|
|
container=container,
|
|
|
current_location=current_location,
|
|
|
target_location=target_location,
|
|
|
- location_group_id=group_id,
|
|
|
- access_priority=access_priority,
|
|
|
+ location_group_id=group_id or DEFAULT_LOCATION_GROUP_ID,
|
|
|
left_priority=left_priority,
|
|
|
right_priority=right_priority,
|
|
|
task_type=task_type,
|
|
|
- order_number=order_number,
|
|
|
+ order_number=order_number_value or DEFAULT_ORDER_NUMBER,
|
|
|
sequence=sequence,
|
|
|
response_data=response_data,
|
|
|
floor=floor,
|
|
|
@@ -2344,13 +2514,18 @@ class OutboundService:
|
|
|
"msg": f"批次 {container['batch_id']} 不存在",
|
|
|
}
|
|
|
month = int(timezone.now().strftime("%Y%m"))
|
|
|
+ location_meta = get_task_location_metadata(
|
|
|
+ "outbound",
|
|
|
+ container_obj.current_location,
|
|
|
+ "103"
|
|
|
+ )
|
|
|
task = ContainerWCSModel(
|
|
|
taskid=OutboundService.generate_task_id(),
|
|
|
batch = OutBoundDetail_obj.bound_batch_number,
|
|
|
batch_out = OutBoundDetail_obj.bound_batch,
|
|
|
bound_list = OutBoundDetail_obj.bound_list,
|
|
|
sequence=index,
|
|
|
- order_number = container['c_number'],
|
|
|
+ order_number = container.get('c_number') or location_meta['order_number'] or DEFAULT_ORDER_NUMBER,
|
|
|
priority=100,
|
|
|
tasknumber = month*100000+tasknumber_index+tasknumber,
|
|
|
container=container_obj.container_code,
|
|
|
@@ -2360,6 +2535,7 @@ class OutboundService:
|
|
|
month=int(timezone.now().strftime("%Y%m")),
|
|
|
message="等待出库",
|
|
|
status=100,
|
|
|
+ location_group_id=location_meta['location_group_id'],
|
|
|
)
|
|
|
layer = None
|
|
|
try:
|
|
|
@@ -2402,13 +2578,18 @@ class OutboundService:
|
|
|
return False
|
|
|
batch_obj = BoundBatchModel.objects.filter(id =batch_id).first()
|
|
|
month = int(timezone.now().strftime("%Y%m"))
|
|
|
+ location_meta = get_task_location_metadata(
|
|
|
+ "check",
|
|
|
+ container_obj.current_location,
|
|
|
+ "103"
|
|
|
+ )
|
|
|
task = ContainerWCSModel(
|
|
|
taskid=OutboundService.generate_task_id(),
|
|
|
batch = batch_obj,
|
|
|
batch_out = None,
|
|
|
bound_list = None,
|
|
|
sequence=index,
|
|
|
- order_number = container['c_number'],
|
|
|
+ order_number = container.get('c_number') or location_meta['order_number'] or DEFAULT_ORDER_NUMBER,
|
|
|
priority=100,
|
|
|
tasknumber = month*100000+tasknumber_index+tasknumber,
|
|
|
container=container_obj.container_code,
|
|
|
@@ -2418,6 +2599,7 @@ class OutboundService:
|
|
|
month=int(timezone.now().strftime("%Y%m")),
|
|
|
message="等待出库",
|
|
|
status=100,
|
|
|
+ location_group_id=location_meta['location_group_id'],
|
|
|
)
|
|
|
tasknumber_index += 1
|
|
|
tasks.append(task)
|
|
|
@@ -2435,16 +2617,29 @@ class OutboundService:
|
|
|
|
|
|
# 插入新任务
|
|
|
for new_task_data in new_tasks:
|
|
|
+ target_location = new_task_data.get('target_location', 'OUT01')
|
|
|
+ location_meta = get_task_location_metadata(
|
|
|
+ "outbound",
|
|
|
+ new_task_data['current_location'],
|
|
|
+ target_location
|
|
|
+ )
|
|
|
+ order_number = new_task_data.get('order_number')
|
|
|
+ if order_number in (None, 0):
|
|
|
+ order_number = location_meta['order_number']
|
|
|
+ if order_number in (None, 0):
|
|
|
+ order_number = DEFAULT_ORDER_NUMBER
|
|
|
new_task = ContainerWCSModel(
|
|
|
taskid=OutboundService.generate_task_id(),
|
|
|
priority=new_task_data.get('priority', 100),
|
|
|
+ order_number=order_number,
|
|
|
container=new_task_data['container'],
|
|
|
current_location=new_task_data['current_location'],
|
|
|
- target_location=new_task_data.get('target_location', 'OUT01'),
|
|
|
+ target_location=target_location,
|
|
|
tasktype="outbound",
|
|
|
month=int(timezone.now().strftime("%Y%m")),
|
|
|
message="等待出库",
|
|
|
status=100,
|
|
|
+ location_group_id=location_meta['location_group_id'],
|
|
|
)
|
|
|
# 找到插入位置
|
|
|
insert_pos = 0
|
|
|
@@ -2496,14 +2691,6 @@ class OutboundService:
|
|
|
)
|
|
|
).order_by('batch_out_id_for_sort', 'sequence')
|
|
|
|
|
|
- def extract_layer(location):
|
|
|
- """从位置字符串中提取楼层信息"""
|
|
|
- try:
|
|
|
- parts = str(location).split('-')
|
|
|
- return parts[3] if len(parts) >= 4 else None
|
|
|
- except Exception:
|
|
|
- return None
|
|
|
-
|
|
|
pending_tasks = get_pending_tasks()
|
|
|
|
|
|
if not pending_tasks.exists():
|
|
|
@@ -2514,6 +2701,7 @@ class OutboundService:
|
|
|
cfg = DispatchConfig.get_active_config()
|
|
|
cross_floor_limit = max(1, int(cfg.cross_floor_concurrent_limit or 2))
|
|
|
desired_layers = set(initial_layers or [])
|
|
|
+ active_layers = OutboundService._get_running_layers()
|
|
|
# 处理任务列表
|
|
|
# 如果single_task=True,只下发1条;否则批量下发(最多cross_floor_limit条)
|
|
|
processed_count = 0
|
|
|
@@ -2525,25 +2713,32 @@ class OutboundService:
|
|
|
max_skip = max(20, len(desired_layers) * 5)
|
|
|
dispatched_ids = set()
|
|
|
used_layers = set()
|
|
|
+ blocked_layers = set()
|
|
|
|
|
|
while processed_count < max_tasks and skip_count < max_skip:
|
|
|
# 重新获取待处理任务(因为可能有任务被跳过)
|
|
|
pending_tasks = get_pending_tasks().exclude(pk__in=dispatched_ids)
|
|
|
if not pending_tasks.exists():
|
|
|
break
|
|
|
- if desired_layers and used_layers.issuperset(desired_layers):
|
|
|
- logger.info("已完成初始多楼层任务的分发")
|
|
|
- break
|
|
|
+ if desired_layers:
|
|
|
+ effective_layers = desired_layers - blocked_layers
|
|
|
+ if not effective_layers:
|
|
|
+ logger.info("初始楼层均存在执行中的任务,暂不新增同楼层下发")
|
|
|
+ break
|
|
|
+ if used_layers.issuperset(effective_layers):
|
|
|
+ logger.info("已完成初始多楼层任务的分发")
|
|
|
+ break
|
|
|
|
|
|
# 如果single_task=True且提供了preferred_layer,优先选择同楼层的任务
|
|
|
next_task = None
|
|
|
if desired_layers:
|
|
|
- remaining_layers = desired_layers - used_layers
|
|
|
- target_layers = remaining_layers if remaining_layers else desired_layers
|
|
|
+ effective_layers = desired_layers - blocked_layers
|
|
|
+ remaining_layers = effective_layers - used_layers
|
|
|
+ target_layers = remaining_layers if remaining_layers else effective_layers
|
|
|
prioritized = []
|
|
|
fallback = []
|
|
|
for task in pending_tasks:
|
|
|
- task_layer = extract_layer(task.current_location)
|
|
|
+ task_layer = OutboundService._extract_layer(task.current_location)
|
|
|
if task_layer in target_layers:
|
|
|
prioritized.append(task)
|
|
|
else:
|
|
|
@@ -2560,7 +2755,7 @@ class OutboundService:
|
|
|
same_layer_tasks = []
|
|
|
other_layer_tasks = []
|
|
|
for task in pending_tasks:
|
|
|
- task_layer = extract_layer(task.current_location)
|
|
|
+ task_layer = OutboundService._extract_layer(task.current_location)
|
|
|
if task_layer == preferred_layer:
|
|
|
same_layer_tasks.append(task)
|
|
|
else:
|
|
|
@@ -2586,7 +2781,7 @@ class OutboundService:
|
|
|
dispatched_ids.add(next_task.pk)
|
|
|
location = next_task.current_location
|
|
|
# 解析楼层(假设格式 Wxx-row-col-layer)
|
|
|
- task_layer = extract_layer(location)
|
|
|
+ task_layer = OutboundService._extract_layer(location)
|
|
|
|
|
|
if location == '103' or location == '203':
|
|
|
logger.info(f"需要跳过该任务: {next_task.taskid}, 位置: {location}")
|
|
|
@@ -2597,6 +2792,12 @@ class OutboundService:
|
|
|
# 跳过这个任务后,继续处理下一个
|
|
|
continue
|
|
|
|
|
|
+ if task_layer and task_layer in active_layers:
|
|
|
+ logger.info(f"楼层 {task_layer} 已有执行中的任务,跳过下发: {next_task.taskid}")
|
|
|
+ blocked_layers.add(task_layer)
|
|
|
+ skip_count += 1
|
|
|
+ continue
|
|
|
+
|
|
|
# 跨楼层并发控制:同一轮不允许重复楼层(仅批量下发时生效)
|
|
|
if not single_task and not desired_layers and task_layer and task_layer in used_layers:
|
|
|
skip_count += 1
|
|
|
@@ -2619,6 +2820,7 @@ class OutboundService:
|
|
|
processed_count += 1
|
|
|
if task_layer:
|
|
|
used_layers.add(task_layer)
|
|
|
+ active_layers.add(task_layer)
|
|
|
logger.info(f"成功下发任务: {next_task.taskid}, 批次: {next_task.batch_out_id if next_task.batch_out else '无批次'}")
|
|
|
except Exception as e:
|
|
|
logger.error(f"任务处理失败: {next_task.taskid}, 错误: {str(e)}")
|