Browse Source

Refactor main.py to enable API call for updating slice status and enhance argument parsing. Update funcs.py for improved code readability and error handling in Blender execution.

master
dongchangxi 3 weeks ago
parent
commit
d43a50e14d
  1. 25
      script/factory_sliceing_v2/main.py
  2. 106
      script/factory_sliceing_v2/utils/funcs.py

25
script/factory_sliceing_v2/main.py

@ -3,6 +3,7 @@ import redis
import oss2,time,sys import oss2,time,sys
import requests import requests
import argparse,json import argparse,json
from utils.funcs import requestApiToUpdateSliceStatus
# 将当前脚本所在目录添加到 Python 路径,以便导入 utils 模块 # 将当前脚本所在目录添加到 Python 路径,以便导入 utils 模块
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
@ -50,14 +51,14 @@ def step1(versionId):
objCounts += 1 objCounts += 1
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 下载处理完成的obj文件数量: {objCounts}') print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 下载处理完成的obj文件数量: {objCounts}')
# requestApiToUpdateSliceStatus(versionId,objCounts) requestApiToUpdateSliceStatus(versionId,objCounts)
# 读取 队列中一个数据出来 # 读取 队列中一个数据出来
def main(work_dir=None): def main(work_dir=None):
global currentDir
# 如果指定了工作目录,使用指定的目录 # 如果指定了工作目录,使用指定的目录
if work_dir: if work_dir:
work_dir = os.path.abspath(work_dir) work_dir = os.path.abspath(work_dir)
@ -122,13 +123,13 @@ def testMain():
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 处理完成,res={res}') print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 处理完成,res={res}')
if __name__ == '__main__': if __name__ == '__main__':
testMain() #testMain()
# parser = argparse.ArgumentParser(description='排版打印订单处理程序') parser = argparse.ArgumentParser(description='排版打印订单处理程序')
# parser.add_argument( parser.add_argument(
# '--work-dir', '--work-dir',
# type=str, type=str,
# default=None, default=None,
# help='指定工作目录(磁盘路径),例如: D:/work 或 /Users/username/work。如果不指定,则使用脚本所在目录' help='指定工作目录(磁盘路径),例如: D:/work 或 /Users/username/work。如果不指定,则使用脚本所在目录'
# ) )
# args = parser.parse_args() args = parser.parse_args()
# main(work_dir=args.work_dir) main(work_dir=args.work_dir)

106
script/factory_sliceing_v2/utils/funcs.py

@ -1,14 +1,18 @@
import requests,time import requests
import os,platform import time
import os
import platform
import shutil import shutil
import json import json
import shlex import shlex
import subprocess
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
from .oss_redis import ossClient from .oss_redis import ossClient
from .logs import log from .logs import log
from .oss_func import download_file_with_check, checkFileExists from .oss_func import download_file_with_check, checkFileExists
from .changeFiles import changeObjFile, changeMtlFile from .changeFiles import changeObjFile, changeMtlFile
from .small_machine_transform import transform_save from .small_machine_transform import transform_save
ENV = 'prod' ENV = 'prod'
url = 'https://mp.api.suwa3d.com' url = 'https://mp.api.suwa3d.com'
if ENV == 'dev': if ENV == 'dev':
@ -29,6 +33,7 @@ def getDownloadDirByPrintId(printIds):
else: else:
return False return False
# 根据批次id,进行切片中的状态变更 # 根据批次id,进行切片中的状态变更
def requestApiToUpdateSliceStatus(versionId, downloadCounts): def requestApiToUpdateSliceStatus(versionId, downloadCounts):
api_url = f"{url}/api/printTypeSettingOrder/updateBatchSliceing?batch_id={versionId}&download_counts=" + str(downloadCounts) api_url = f"{url}/api/printTypeSettingOrder/updateBatchSliceing?batch_id={versionId}&download_counts=" + str(downloadCounts)
@ -48,6 +53,7 @@ def requestApiToUpdateSliceStatus(versionId,downloadCounts):
log(f'状态变更请求异常, error={str(e)}') log(f'状态变更请求异常, error={str(e)}')
return False return False
# 判断是否上传了 JSON 文件 # 判断是否上传了 JSON 文件
def checkJsonFileExists(versionId): def checkJsonFileExists(versionId):
log(f"检测文件和图片是否存在, versionId={versionId}") log(f"检测文件和图片是否存在, versionId={versionId}")
@ -59,7 +65,6 @@ def checkJsonFileExists(versionId):
log(f'JSON文件不存在: {jsonFilePath}') log(f'JSON文件不存在: {jsonFilePath}')
return False, False return False, False
if not ossClient().object_exists(jpgFilePath): if not ossClient().object_exists(jpgFilePath):
log(f'JPG文件不存在: {jpgFilePath}') log(f'JPG文件不存在: {jpgFilePath}')
return False, False return False, False
@ -67,19 +72,20 @@ def checkJsonFileExists(versionId):
return jsonFilePath, jpgFilePath return jsonFilePath, jpgFilePath
# 检测本地文件是否存在 # 检测本地文件是否存在
def checkLocalFileExists(localFilePath): def checkLocalFileExists(localFilePath):
if not os.path.exists(localFilePath): if not os.path.exists(localFilePath):
return False return False
return True return True
# 读取JSON文件内容 # 读取JSON文件内容
def readJsonFile(localFilePath): def readJsonFile(localFilePath):
with open(localFilePath, 'r', encoding='utf-8') as f: with open(localFilePath, 'r', encoding='utf-8') as f:
jsonData = json.load(f) jsonData = json.load(f)
return jsonData return jsonData
# 根据批次ID,获取批次信息 # 根据批次ID,获取批次信息
def getBatchInfo(versionId): def getBatchInfo(versionId):
url1 = f"{url}/api/printTypeSettingOrder/getBatchInfoAndPrintMachineInfoByBatchId?batch_id={versionId}" url1 = f"{url}/api/printTypeSettingOrder/getBatchInfoAndPrintMachineInfoByBatchId?batch_id={versionId}"
@ -91,6 +97,7 @@ def getBatchInfo(versionId):
else: else:
return False return False
# 下载文件,读取文件内容,并且文件迁移至正确的目录里,不再放在临时目录里 # 下载文件,读取文件内容,并且文件迁移至正确的目录里,不再放在临时目录里
def downloadJsonAndJpgFileAndMoveToCorrectDir(versionId, currentDir): def downloadJsonAndJpgFileAndMoveToCorrectDir(versionId, currentDir):
jsonFilePath, jpgFilePath = checkJsonFileExists(versionId) jsonFilePath, jpgFilePath = checkJsonFileExists(versionId)
@ -139,7 +146,6 @@ def downloadJsonAndJpgFileAndMoveToCorrectDir(versionId,currentDir):
if not os.path.exists(jsonSubDir): if not os.path.exists(jsonSubDir):
os.makedirs(jsonSubDir) os.makedirs(jsonSubDir)
# 将数据移动过来 # 将数据移动过来
shutil.move(localFilePath, os.path.join(jsonSubDir, '3DPrintLayout.json')) shutil.move(localFilePath, os.path.join(jsonSubDir, '3DPrintLayout.json'))
shutil.move(localJpgFilePath, os.path.join(jsonSubDir, f'{versionId}.jpg')) shutil.move(localJpgFilePath, os.path.join(jsonSubDir, f'{versionId}.jpg'))
@ -186,8 +192,6 @@ def getJsonData(dirNewName):
log(f"数据不完整, orderId={orderId}, pid={pid}, printId={printId}, size={size}, counts={counts}") log(f"数据不完整, orderId={orderId}, pid={pid}, printId={printId}, size={size}, counts={counts}")
return False return False
# 创建数据结构 # 创建数据结构
modelInfo = { modelInfo = {
"orderId": orderId, "orderId": orderId,
@ -277,6 +281,7 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
beginTime = time.time() beginTime = time.time()
objsLocal = "" objsLocal = ""
for objFiles in arrDownloadFiles: for objFiles in arrDownloadFiles:
downloadBeginTime = time.time()
# 判断 mtl 和 jpg 文件是否存在,存在就不在下载了 # 判断 mtl 和 jpg 文件是否存在,存在就不在下载了
if "mtl" in objFiles["localPath"] or "jpg" in objFiles["localPath"]: if "mtl" in objFiles["localPath"] or "jpg" in objFiles["localPath"]:
if os.path.exists(objFiles["localPath"]): if os.path.exists(objFiles["localPath"]):
@ -288,11 +293,13 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
log(error_msg) log(error_msg)
return False, error_msg return False, error_msg
log(f"下载文件耗时: {time.time() - downloadBeginTime}秒 - {objFiles['localPath']}")
# 下载成功之后要修改文件之间的关联路径 # 下载成功之后要修改文件之间的关联路径
if objFiles["localPath"].endswith(".obj"): if objFiles["localPath"].endswith(".obj"):
beginChangeTime = time.time()
objsLocal = objFiles["localPath"] objsLocal = objFiles["localPath"]
changeObjFile(objFiles["localPath"], f"{orderId}_{pid}.mtl") changeObjFile(objFiles["localPath"], f"{orderId}_{pid}.mtl")
log(f"修改obj关联耗时: {time.time() - beginChangeTime}秒 - {objFiles['localPath']}")
if objFiles["localPath"].endswith(".mtl"): if objFiles["localPath"].endswith(".mtl"):
changeMtlFile(objFiles["localPath"], f"{orderId}_{pid}Tex1.jpg") changeMtlFile(objFiles["localPath"], f"{orderId}_{pid}Tex1.jpg")
@ -313,18 +320,56 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
# 获取 small_machine_transform.py 的绝对路径 # 获取 small_machine_transform.py 的绝对路径
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
transform_script_path = os.path.join(script_dir, 'small_machine_transform.py') transform_script_path = os.path.join(script_dir, 'small_machine_transform.py')
# 将 homo_matrix 转换为 JSON 字符串,并对路径进行转义
# 将 homo_matrix 转换为 JSON 字符串
homo_matrix_json = json.dumps(homo_matrix) homo_matrix_json = json.dumps(homo_matrix)
# 使用 shlex.quote 来安全地转义路径和参数
transform_script_path_quoted = shlex.quote(transform_script_path) # 构建命令参数列表(使用列表形式,避免 shell 转义问题)
objsLocal_quoted = shlex.quote(objsLocal) cmd = [
homo_matrix_quoted = shlex.quote(homo_matrix_json) blender_bin_path,
error = os.system(f"{blender_bin_path} -b -P {transform_script_path_quoted} -- --objPathName={objsLocal_quoted} --trans={homo_matrix_quoted}") '--background',
if error != 0: '--python',
error_msg = f"调用blender 执行 python 文件失败, error={error}, fileName={fileName}" transform_script_path,
'--',
f'--objPathName={objsLocal}',
f'--trans={homo_matrix_json}'
]
log(f"执行 Blender 命令: {' '.join(cmd[:4])} ... (参数已隐藏)")
try:
# 使用 subprocess 执行命令,捕获输出和错误
# 在 Windows 上,Blender 输出可能是 UTF-8,需要指定编码并处理错误
result = subprocess.run(
cmd,
capture_output=True,
text=True,
encoding='utf-8', # 指定 UTF-8 编码
errors='replace', # 遇到无法解码的字符时替换为占位符,而不是抛出异常
timeout=300, # 5分钟超时
check=False # 不自动抛出异常,手动检查返回码
)
if result.returncode != 0:
error_output = result.stderr if result.stderr else result.stdout
error_msg = f"调用blender 执行 python 文件失败, error={result.returncode}, fileName={fileName}"
if error_output:
error_msg += f", 错误信息: {error_output[:500]}" # 限制错误信息长度
log(error_msg)
return False, error_msg
log(f"调用blender 执行 python 文件成功, fileName={fileName}")
if result.stdout:
log(f"Blender 输出: {result.stdout[:200]}") # 记录部分输出用于调试
except subprocess.TimeoutExpired:
error_msg = f"调用blender 执行超时(超过5分钟), fileName={fileName}"
log(error_msg)
return False, error_msg
except Exception as e:
error_msg = f"调用blender 执行时发生异常: {str(e)}, fileName={fileName}"
log(error_msg) log(error_msg)
return False, error_msg return False, error_msg
log(f"调用blender 执行 python 文件成功, error={error}, fileName={fileName}")
timeEnd = time.time() timeEnd = time.time()
log(f"转换数据时间: 耗时{timeEnd - timeBegin}秒 - {objsLocal}") log(f"转换数据时间: 耗时{timeEnd - timeBegin}秒 - {objsLocal}")
@ -337,7 +382,7 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
# json文件进行下载对应的数据 和转换数据,传递目录路径 # json文件进行下载对应的数据 和转换数据,传递目录路径
def downloadDataByOssAndTransformSave(dirNewName,isSmallMachine=False, max_workers=10): def downloadDataByOssAndTransformSave(dirNewName, isSmallMachine=False, max_workers=1):
listData = getJsonData(dirNewName) listData = getJsonData(dirNewName)
if not listData: if not listData:
log(f"获取数据失败, dirNewName={dirNewName}") log(f"获取数据失败, dirNewName={dirNewName}")
@ -405,14 +450,35 @@ def downloadDataByOssAndTransformSave(dirNewName,isSmallMachine=False, max_worke
def findBpyModule(): def findBpyModule():
# 返回 Blender 可执行文件路径(macOS) # 返回 Blender 可执行文件路径
blender_bin_path = '/Applications/Blender.app/Contents/MacOS/Blender' blender_bin_path = '/Applications/Blender.app/Contents/MacOS/Blender'
# 判断当前是 windows 还是 macOS # 判断当前是 windows 还是 macOS
if platform.system() == 'Windows': if platform.system() == 'Windows':
# Windows 上常见的 Blender 安装路径
possible_paths = [
'C:\\Program Files\\Blender Foundation\\Blender 5.0\\blender.exe',
'C:\\Program Files\\Blender Foundation\\Blender 4.4\\blender.exe',
'C:\\Program Files\\Blender Foundation\\Blender 4.3\\blender.exe',
'C:\\Program Files\\Blender Foundation\\Blender 4.2\\blender.exe',
'C:\\Program Files\\Blender Foundation\\Blender 4.1\\blender.exe',
'C:\\Program Files\\Blender Foundation\\Blender 4.0\\blender.exe',
]
# 查找存在的路径
for path in possible_paths:
if os.path.exists(path):
blender_bin_path = path
break
else:
# 如果都没找到,使用默认路径
blender_bin_path = 'C:\\Program Files\\Blender Foundation\\Blender 5.0\\blender.exe' blender_bin_path = 'C:\\Program Files\\Blender Foundation\\Blender 5.0\\blender.exe'
log(f"警告: 未找到 Blender 可执行文件,使用默认路径: {blender_bin_path}")
else: else:
blender_bin_path = '/Applications/Blender.app/Contents/MacOS/Blender' blender_bin_path = '/Applications/Blender.app/Contents/MacOS/Blender'
# 检查路径是否存在
if not os.path.exists(blender_bin_path):
error_msg = f"Blender 可执行文件不存在: {blender_bin_path}"
log(error_msg)
raise FileNotFoundError(error_msg)
return blender_bin_path return blender_bin_path
Loading…
Cancel
Save