修补登录问题

This commit is contained in:
靳中伟 2025-09-10 15:45:35 +08:00
parent 6f4d9d8181
commit 674da470d3
42 changed files with 19132 additions and 565 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

15720
logs/app.log.2025-09-09 Normal file

File diff suppressed because it is too large Load Diff

View File

@ -93,7 +93,8 @@ class TaskBasicInfo(BaseModel):
label: str = Field(..., description="任务名称")
remark: Optional[str] = Field(None, description="备注")
releaseSites: bool = Field(True, description="是否释放站点")
period: Optional[int]= Field(0, description="周期时间(定时任务 毫秒)")
delay: Optional[int]= Field(0, description="延迟时间(定时任务 毫秒)")
@validator('label')
def label_must_not_be_empty_or_whitespace(cls, v):
"""验证任务名称不为空且不只包含空白字符"""

View File

@ -512,12 +512,364 @@ async def get_all_movable_vehicles_in_loop(vehicle_list: list, location_list: li
}
async def judgment_condition(location: str, batteryLevel: float, threshold: float, back_task_id: str) -> dict:
if location.find("CP") != -1 and batteryLevel >= threshold:
return {"bool": True}
else:
async def judgment_condition(location: str, batteryLevel: float, threshold: float, back_task_id: str, agv_id: str) -> dict:
"""
判断是否满足执行条件
Args:
location: 当前位置
batteryLevel: 电池电量
threshold: 电量阈值
back_task_id: 任务模板ID
agv_id: AGV设备ID
Returns:
dict: {"bool": True/False}
"""
from data.session import get_async_session
from data.models.taskrecord import VWEDTaskRecord
from data.enum.task_record_enum import TaskStatus
from sqlalchemy import select
from utils.logger import get_logger
logger = get_logger("scripts.user_save.test1")
try:
# 首先检查位置和电量条件
if location.find("CP") == -1 or batteryLevel < threshold:
return {"bool": False}
# pass
# 检查是否有相同任务模板的运行任务且AGV相同
async with get_async_session() as session:
stmt = select(VWEDTaskRecord).where(
VWEDTaskRecord.def_id == back_task_id,
VWEDTaskRecord.agv_id == agv_id,
VWEDTaskRecord.status == TaskStatus.RUNNING,
VWEDTaskRecord.is_deleted == False
)
result = await session.execute(stmt)
running_tasks = result.scalars().all()
if running_tasks:
logger.info(f"发现相同任务模板的运行任务AGV: {agv_id}, 任务模板: {back_task_id}, 运行任务数量: {len(running_tasks)}")
return {"bool": False}
logger.info(f"条件检查通过,位置: {location}, 电量: {batteryLevel}, 阈值: {threshold}, AGV: {agv_id}")
return {"bool": True}
except Exception as e:
logger.error(f"判断条件时发生错误: {str(e)}")
return {"bool": False}
async def check_location_lock_status(location_id: str) -> dict:
"""
查询库位是否被锁定
Args:
location_id: 库位IDlayer_name字段的值
Returns:
dict: {
"is_locked": bool, # 是否被锁定
"locked_by": str or None, # 锁定者如果未锁定则为None
"location_exists": bool, # 库位是否存在
"message": str # 状态描述
}
"""
from data.session import get_async_session
from data.models.operate_point_layer import OperatePointLayer
from sqlalchemy import select
from utils.logger import get_logger
logger = get_logger("scripts.user_save.test1")
try:
if not location_id:
return {
"is_locked": False,
"locked_by": None,
"location_exists": False,
"message": "库位ID参数为空"
}
async with get_async_session() as session:
# 查询指定库位的锁定状态
stmt = select(OperatePointLayer).where(
OperatePointLayer.layer_name == location_id,
OperatePointLayer.is_deleted == False
).limit(1)
result = await session.execute(stmt)
location = result.scalar_one_or_none()
if not location:
logger.warning(f"未找到库位: {location_id}")
return {
"is_locked": False,
"locked_by": None,
"location_exists": False,
"message": f"库位 {location_id} 不存在"
}
is_locked = bool(location.is_locked)
locked_by = location.locked_by if is_locked else None
if is_locked:
message = f"库位 {location_id} 已被锁定,锁定者: {locked_by}"
logger.info(message)
else:
message = f"库位 {location_id} 未被锁定"
logger.info(message)
return {
"is_locked": is_locked,
"locked_by": locked_by,
"location_exists": True,
"message": message
}
except Exception as e:
error_msg = f"查询库位锁定状态时发生错误: {str(e)}"
logger.error(error_msg)
return {
"is_locked": False,
"locked_by": None,
"location_exists": False,
"message": error_msg
}
async def unlock_locations_when_last(current_location: str, location_list: list) -> dict:
"""
检查当前库位是否为库位集合的最后一个如果是则解锁所有库位
Args:
current_location: 当前库位名称
location_list: 按照获取顺序的库位集合列表
Returns:
dict: {
"is_last": bool, # 是否为最后一个库位
"unlocked": bool, # 是否执行了解锁操作
"unlocked_count": int, # 解锁的库位数量
"message": str # 状态描述
}
"""
from data.session import get_db
from services.operate_point_service import OperatePointService
from routes.model.operate_point_model import BatchStorageLocationStatusUpdateRequest, StorageLocationActionEnum
from utils.logger import get_logger
logger = get_logger("scripts.user_save.test1")
try:
if not current_location:
return {
"is_last": False,
"unlocked": False,
"unlocked_count": 0,
"message": "当前库位参数为空"
}
if not location_list:
return {
"is_last": False,
"unlocked": False,
"unlocked_count": 0,
"message": "库位集合列表为空"
}
# 检查当前库位是否在列表中
if current_location not in location_list:
return {
"is_last": False,
"unlocked": False,
"unlocked_count": 0,
"message": f"当前库位 {current_location} 不在库位集合中"
}
# 检查是否为最后一个库位
is_last = current_location == location_list[-1]
if not is_last:
return {
"is_last": False,
"unlocked": False,
"unlocked_count": 0,
"message": f"当前库位 {current_location} 不是最后一个库位(最后一个为:{location_list[-1]}"
}
# 当前库位是最后一个,执行批量解锁操作
logger.info(f"当前库位 {current_location} 是最后一个库位,开始解锁所有库位")
try:
# 创建批量解锁请求对象
batch_request = BatchStorageLocationStatusUpdateRequest(
layer_names=location_list,
action=StorageLocationActionEnum.UNLOCK,
reason="任务完成,自动解锁所有库位"
)
# 使用数据库会话调用服务方法
db = next(get_db())
try:
# 调用批量更新库位状态服务
result = OperatePointService.batch_update_storage_location_status(db, batch_request)
logger.info(f"批量解锁完成:成功 {result.success_count} 个,失败 {result.failed_count}")
return {
"is_last": True,
"unlocked": True,
"unlocked_count": result.success_count,
"message": f"当前库位是最后一个,已执行批量解锁:成功 {result.success_count} 个库位,失败 {result.failed_count} 个库位"
}
finally:
db.close()
except Exception as e:
logger.error(f"执行批量解锁时发生异常: {str(e)}")
return {
"is_last": True,
"unlocked": False,
"unlocked_count": 0,
"message": f"当前库位是最后一个,但解锁操作失败:{str(e)}"
}
except Exception as e:
error_msg = f"检查库位和执行解锁时发生错误: {str(e)}"
logger.error(error_msg)
return {
"is_last": False,
"unlocked": False,
"unlocked_count": 0,
"message": error_msg
}
async def check_vehicle_at_location(vehicle_name: str, location_list: list, vehicle_current_position: str) -> dict:
"""
检查小车是否在被其锁定的库位对应的站点位置
Args:
vehicle_name: 小车名称
location_list: 库位列表
vehicle_current_position: 小车当前所在的位置/站点
Returns:
dict: {
"at_locked_location": bool, # 是否在被锁定的库位对应站点
"locked_location": str or None, # 被锁定的库位名称
"location_station": str or None,# 库位对应的站点名称
"vehicle_position": str, # 小车当前位置
"message": str # 状态描述
}
"""
from data.session import get_async_session
from data.models.operate_point_layer import OperatePointLayer
from sqlalchemy import select
from utils.logger import get_logger
logger = get_logger("scripts.user_save.test1")
try:
if not vehicle_name:
return {
"at_locked_location": False,
"locked_location": None,
"location_station": None,
"vehicle_position": vehicle_current_position,
"message": "小车名称参数为空"
}
if not location_list:
return {
"at_locked_location": False,
"locked_location": None,
"location_station": None,
"vehicle_position": vehicle_current_position,
"message": "库位列表为空"
}
if not vehicle_current_position:
return {
"at_locked_location": False,
"locked_location": None,
"location_station": None,
"vehicle_position": vehicle_current_position,
"message": "小车当前位置参数为空"
}
async with get_async_session() as session:
# 查询库位列表中被当前小车锁定的库位
stmt = select(OperatePointLayer).where(
OperatePointLayer.layer_name.in_(location_list),
OperatePointLayer.is_deleted == False,
OperatePointLayer.is_locked == True,
OperatePointLayer.locked_by == vehicle_name
)
result = await session.execute(stmt)
locked_locations = result.scalars().all()
if not locked_locations:
logger.info(f"小车 {vehicle_name} 未锁定库位列表中的任何库位")
return {
"at_locked_location": False,
"locked_location": None,
"location_station": None,
"vehicle_position": vehicle_current_position,
"message": f"小车 {vehicle_name} 未锁定库位列表中的任何库位"
}
# 检查每个被锁定的库位
for locked_location in locked_locations:
location_station = locked_location.station_name
location_layer_name = locked_location.layer_name
logger.info(f"检查小车 {vehicle_name} 锁定的库位 {location_layer_name},对应站点: {location_station},小车当前位置: {vehicle_current_position}")
# 检查小车当前位置是否与库位对应的站点匹配
if vehicle_current_position == location_station:
logger.info(f"小车 {vehicle_name} 在被锁定的库位 {location_layer_name} 对应的站点 {location_station}")
return {
"at_locked_location": False,
"locked_location": location_layer_name,
"location_station": location_station,
"vehicle_position": vehicle_current_position,
"message": f"小车 {vehicle_name} 在被锁定的库位 {location_layer_name} 对应的站点 {location_station}"
}
# 如果有锁定库位但位置不匹配
locked_location_info = []
for locked_location in locked_locations:
locked_location_info.append(f"{locked_location.layer_name}(站点:{locked_location.station_name})")
message = f"小车 {vehicle_name} 不在被锁定库位的站点,当前位置: {vehicle_current_position},已锁定库位: {', '.join(locked_location_info)}"
logger.info(message)
return {
"at_locked_location": True,
"locked_location": locked_locations[0].layer_name if locked_locations else None,
"location_station": locked_locations[0].station_name if locked_locations else None,
"vehicle_position": vehicle_current_position,
"message": message
}
except Exception as e:
error_msg = f"检查小车位置时发生错误: {str(e)}"
logger.error(error_msg)
return {
"at_locked_location": False,
"locked_location": None,
"location_station": None,
"vehicle_position": vehicle_current_position,
"message": error_msg
}
# 测试示例(注释掉的测试代码)
# async def test_movable_functions():
# """测试可移动小车功能的示例代码"""

View File

@ -27,7 +27,7 @@ from utils.logger import get_logger
from data.enum.call_device_enum import CallDeviceStatus, CallDeviceButtonStatus, CallDeviceButtonType
from data.enum.task_record_enum import TaskStatus
from config.settings import settings
from services.sync_service import set_task_terminated, get_login_token, refresh_token_if_needed
from services.sync_service import set_task_terminated, get_login_token, refresh_token_if_needed, refresh_token_if_needed_sync
from config.tf_api_config import TF_API_BASE_URL, TF_API_TOKEN_HEADER, TF_API_TOKEN
# 设置日志
@ -64,6 +64,20 @@ async def get_tf_api_token() -> str:
return TF_API_TOKEN
def get_tf_api_token_sync() -> str:
try:
# 尝试刷新或获取新token
token = refresh_token_if_needed_sync()
if token:
logger.debug("成功获取动态token")
return token
except Exception as e:
logger.warning(f"获取动态token失败: {str(e)}")
# 如果获取失败使用配置中的默认token
logger.info("使用默认配置中的token")
return TF_API_TOKEN
class CallDeviceService:
"""
呼叫器设备服务类
@ -1917,12 +1931,12 @@ class CallDeviceService:
# 调用复位API
url = f"{api_url}{settings.CALLDEVICE_API_ENDPOINTS['set_device_state']}"
# tf_api_token = await get_tf_api_token()
tf_api_token = get_tf_api_token_sync()
headers = {
"Content-Type": "application/json",
"User-Agent": "CallDeviceMonitor/1.0",
TF_API_TOKEN_HEADER: TF_API_TOKEN,
TF_API_TOKEN_HEADER: tf_api_token,
"x-tenant-id":"1000"
}

View File

@ -18,6 +18,7 @@ from typing import Dict, List, Any, Optional, Set, Tuple, Union
from datetime import datetime, timedelta
from sqlalchemy import select, update, func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.util import await_only
from config.settings import settings
from data.models.taskdef import VWEDTaskDef
@ -541,7 +542,8 @@ class EnhancedTaskScheduler:
select_agv_type = self.check_task_def_select_agv(task_def.detail)
# 导入同步服务
from services.sync_service import create_task as tf_create_task
from services.sync_service import create_task as tf_create_task, refresh_token_if_needed
token =await refresh_token_if_needed()
# 调用主任务系统创建任务接口
sync_response = await tf_create_task(
@ -550,7 +552,7 @@ class EnhancedTaskScheduler:
is_periodic=task_record.periodic_task,
priority=task_record.priority,
parent_id=task_record.parent_task_record_id or "",
token=task_def.user_token,
token=token,
map_id=task_def.map_id,
is_agv=select_agv_type
)

View File

@ -692,7 +692,7 @@ class GetBatteryLevelBlockHandler(RobotBlockHandler):
from services.sync_service import get_amr_info
# 获取关键参数用于验证
vehicle = input_params.get("vehicle")
vehicle = str(input_params.get("vehicle")).strip()
# 参数检查
if not vehicle:
@ -709,7 +709,8 @@ class GetBatteryLevelBlockHandler(RobotBlockHandler):
if amr_info_result and amr_info_result.get("success", False):
# 获取AMR列表
amr_list = amr_info_result.get("result", [])
# print(amr_list, "==")
# print(vehicle, "====")
# 根据机器人名称查找对应的AMR信息
target_amr = None
for amr in amr_list:

View File

@ -23,6 +23,8 @@ from services.execution.task_context import TaskContext
from services.execution.block_executor import BlockExecutor
from data.enum.task_record_enum import TaskStatus
from utils.logger import get_logger
from services.sync_service import set_task_in_progress, set_task_completed, set_task_failed, set_task_terminated, \
refresh_token_if_needed
# 获取日志记录器
logger = get_logger("services.execution.task_executor")
@ -95,14 +97,14 @@ class TaskExecutor:
# 解析任务输入参数
input_params = json.loads(self.task_record.input_params) if self.task_record.input_params else {}
# self.task_def.map_id
token = await refresh_token_if_needed()
# 创建任务上下文
self.task_context = TaskContext(
task_record_id=self.task_record_id,
task_def_id=self.task_def.id,
input_params=input_params,
variables={},
token=self.task_def.user_token,
token=token,
map_id=self.task_def.map_id
)
@ -535,20 +537,19 @@ class TaskExecutor:
task_detail: 任务详情
"""
try:
from services.sync_service import set_task_in_progress, set_task_completed, set_task_failed, set_task_terminated
token = await refresh_token_if_needed()
if not self.check_task_def_select_agv(task_detail):
if status == TaskStatus.RUNNING:
await set_task_in_progress(self.task_record_id, self.task_def.user_token)
await set_task_in_progress(self.task_record_id, token)
elif status == TaskStatus.COMPLETED:
await set_task_completed(self.task_record_id, self.task_def.user_token)
await set_task_completed(self.task_record_id, token)
elif status == TaskStatus.FAILED:
await set_task_failed(self.task_record_id, self.task_def.user_token)
await set_task_failed(self.task_record_id, token)
elif status == TaskStatus.CANCELED:
await set_task_terminated(self.task_record_id, self.task_def.user_token)
await set_task_terminated(self.task_record_id, token)
else:
if status == TaskStatus.FAILED:
await set_task_failed(self.task_record_id, self.task_def.user_token)
await set_task_failed(self.task_record_id, token)
except Exception as e:
logger.error(f"外部API同步失败: {str(e)}")

View File

@ -9,6 +9,7 @@
import json
import aiohttp
import asyncio
import requests
from typing import Dict, Any, Optional
from datetime import datetime
from pydantic import BaseModel
@ -154,6 +155,7 @@ async def create_task(task_record_id: str, task_name: str, is_periodic: bool, pr
# 尝试解析JSON
try:
response_data = json.loads(response_text)
# print(response_data, "-------------============================")
if response_data.get("success"):
logger.info(f"成功同步任务到系统任务: {task_record_id}")
else:
@ -965,6 +967,109 @@ def clear_cached_token():
logger.info("已清除缓存的token")
def get_login_token_sync(username: str = "vwed", password: str = "vwed_123456") -> Optional[str]:
"""
获取登录token (同步版本)
Args:
username: 用户名默认为"vwed"
password: 密码默认为"vwed_123456"
Returns:
Optional[str]: 认证令牌如果获取失败则返回None
"""
global _cached_token, _token_expire_time
# 检查是否有缓存的token且未过期
if _cached_token and _token_expire_time:
current_time = datetime.now().timestamp() * 1000 # 转换为毫秒
if current_time < _token_expire_time - 60000: # 提前1分钟刷新
logger.debug("使用缓存的token")
return _cached_token
# 构造请求参数
request_data = LoginRequest(
username=username,
password=password
)
# 调用接口
url = f"{TFApiConfig.BASE_URL}{TFApiConfig.LOGIN_PATH}"
# 构建请求头
headers = {
"Content-Type": "application/json",
"X-Tenant-Id": 1000
}
try:
logger.info(f"正在获取登录token用户: {username}")
logger.debug(f"登录请求参数: {request_data.model_dump_json()}")
response = requests.post(
url,
json=request_data.model_dump(),
headers=headers,
timeout=TFApiConfig.TIMEOUT
)
# 尝试解析JSON
try:
response_data = response.json()
if response_data.get("success", False):
token = response_data.get("result", {}).get("token")
if token:
# 缓存token设置过期时间为1小时后
_cached_token = token
_token_expire_time = datetime.now().timestamp() * 1000 + 3600000 # 1小时后过期
logger.info("成功获取登录token")
logger.debug(f"Token: {token[:20]}...") # 只记录token的前20个字符
return token
else:
logger.error("登录响应中未找到token")
return None
else:
logger.error(f"登录失败: {response_data.get('message', '未知错误')}")
return None
except ValueError as e:
logger.error(f"解析登录响应JSON失败: {response.text}")
return None
except requests.RequestException as e:
logger.error(f"调用登录接口失败: {str(e)}")
return None
except Exception as e:
logger.error(f"获取登录token时发生错误: {str(e)}")
return None
def refresh_token_if_needed_sync() -> Optional[str]:
"""
根据需要刷新token (同步版本)
Returns:
Optional[str]: 有效的认证令牌如果获取失败则返回None
"""
global _cached_token, _token_expire_time
# 检查token是否即将过期提前5分钟刷新
if _token_expire_time:
current_time = datetime.now().timestamp() * 1000
if current_time >= _token_expire_time - 300000: # 5分钟内过期
logger.info("Token即将过期正在刷新...")
return get_login_token_sync()
# 如果没有token获取新的
if not _cached_token:
logger.info("没有缓存的token正在获取...")
return get_login_token_sync()
return _cached_token
# import asyncio
# task_block_id = "1947858858043117570"
# station_name = "AP1"

View File

@ -567,6 +567,8 @@ class TaskEditService:
"changed": False,
"message": "数据未发生变化",
"id": task_def.id,
"delay": task_def.delay,
"period": task_def.period,
"label": task_def.label,
"remark": task_def.remark,
"releaseSites": bool(task_def.release_sites)
@ -576,6 +578,8 @@ class TaskEditService:
task_def.label = basic_info.label
task_def.remark = basic_info.remark
task_def.release_sites = release_sites_new
task_def.period = basic_info.period
task_def.delay = basic_info.delay
# 保存到数据库
await session.commit()
@ -590,6 +594,8 @@ class TaskEditService:
"id": task_def.id,
"label": task_def.label,
"remark": task_def.remark,
"period": task_def.period,
"delay": task_def.delay,
"releaseSites": bool(task_def.release_sites),
"updateTime": update_time
}

View File

@ -19,6 +19,23 @@ import json
from datetime import datetime
from config.tf_api_config import TF_API_TOKEN, TF_API_BASE_URL
from config.settings import settings
from services.sync_service import refresh_token_if_needed_sync
def get_tf_api_token_sync() -> str:
try:
# 尝试刷新或获取新token
token = refresh_token_if_needed_sync()
# print(token, "================")
if token:
# logger.debug("成功获取动态token")
return token
except Exception as e:
print(f"获取动态token失败: {str(e)}")
# 如果获取失败使用配置中的默认token
print("使用默认配置中的token")
return TF_API_TOKEN
class AlertSyncService:
@ -186,7 +203,7 @@ class AlertSyncService:
Returns:
是否发送成功
"""
# get_tf_api_token()
token = get_tf_api_token_sync()
for attempt in range(self.retry_count):
try:
print("------------------====>>>", alert_data,"<<<<=================")
@ -195,7 +212,7 @@ class AlertSyncService:
json=alert_data,
timeout=self.timeout,
headers={'Content-Type': 'application/json',
'x-access-token': TF_API_TOKEN
'x-access-token': token
}
)
if response.status_code == 200: