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

106
script/factory_sliceing_v2/utils/funcs.py

@ -1,14 +1,18 @@ @@ -1,14 +1,18 @@
import requests,time
import os,platform
import requests
import time
import os
import platform
import shutil
import json
import shlex
import subprocess
from concurrent.futures import ThreadPoolExecutor, as_completed
from .oss_redis import ossClient
from .logs import log
from .oss_func import download_file_with_check, checkFileExists
from .changeFiles import changeObjFile, changeMtlFile
from .small_machine_transform import transform_save
ENV = 'prod'
url = 'https://mp.api.suwa3d.com'
if ENV == 'dev':
@ -29,6 +33,7 @@ def getDownloadDirByPrintId(printIds): @@ -29,6 +33,7 @@ def getDownloadDirByPrintId(printIds):
else:
return False
# 根据批次id,进行切片中的状态变更
def requestApiToUpdateSliceStatus(versionId, downloadCounts):
api_url = f"{url}/api/printTypeSettingOrder/updateBatchSliceing?batch_id={versionId}&download_counts=" + str(downloadCounts)
@ -48,6 +53,7 @@ def requestApiToUpdateSliceStatus(versionId,downloadCounts): @@ -48,6 +53,7 @@ def requestApiToUpdateSliceStatus(versionId,downloadCounts):
log(f'状态变更请求异常, error={str(e)}')
return False
# 判断是否上传了 JSON 文件
def checkJsonFileExists(versionId):
log(f"检测文件和图片是否存在, versionId={versionId}")
@ -59,7 +65,6 @@ def checkJsonFileExists(versionId): @@ -59,7 +65,6 @@ def checkJsonFileExists(versionId):
log(f'JSON文件不存在: {jsonFilePath}')
return False, False
if not ossClient().object_exists(jpgFilePath):
log(f'JPG文件不存在: {jpgFilePath}')
return False, False
@ -67,19 +72,20 @@ def checkJsonFileExists(versionId): @@ -67,19 +72,20 @@ def checkJsonFileExists(versionId):
return jsonFilePath, jpgFilePath
# 检测本地文件是否存在
def checkLocalFileExists(localFilePath):
if not os.path.exists(localFilePath):
return False
return True
# 读取JSON文件内容
def readJsonFile(localFilePath):
with open(localFilePath, 'r', encoding='utf-8') as f:
jsonData = json.load(f)
return jsonData
# 根据批次ID,获取批次信息
def getBatchInfo(versionId):
url1 = f"{url}/api/printTypeSettingOrder/getBatchInfoAndPrintMachineInfoByBatchId?batch_id={versionId}"
@ -91,6 +97,7 @@ def getBatchInfo(versionId): @@ -91,6 +97,7 @@ def getBatchInfo(versionId):
else:
return False
# 下载文件,读取文件内容,并且文件迁移至正确的目录里,不再放在临时目录里
def downloadJsonAndJpgFileAndMoveToCorrectDir(versionId, currentDir):
jsonFilePath, jpgFilePath = checkJsonFileExists(versionId)
@ -139,7 +146,6 @@ def downloadJsonAndJpgFileAndMoveToCorrectDir(versionId,currentDir): @@ -139,7 +146,6 @@ def downloadJsonAndJpgFileAndMoveToCorrectDir(versionId,currentDir):
if not os.path.exists(jsonSubDir):
os.makedirs(jsonSubDir)
# 将数据移动过来
shutil.move(localFilePath, os.path.join(jsonSubDir, '3DPrintLayout.json'))
shutil.move(localJpgFilePath, os.path.join(jsonSubDir, f'{versionId}.jpg'))
@ -186,8 +192,6 @@ def getJsonData(dirNewName): @@ -186,8 +192,6 @@ def getJsonData(dirNewName):
log(f"数据不完整, orderId={orderId}, pid={pid}, printId={printId}, size={size}, counts={counts}")
return False
# 创建数据结构
modelInfo = {
"orderId": orderId,
@ -277,6 +281,7 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine @@ -277,6 +281,7 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
beginTime = time.time()
objsLocal = ""
for objFiles in arrDownloadFiles:
downloadBeginTime = time.time()
# 判断 mtl 和 jpg 文件是否存在,存在就不在下载了
if "mtl" in objFiles["localPath"] or "jpg" in objFiles["localPath"]:
if os.path.exists(objFiles["localPath"]):
@ -288,11 +293,13 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine @@ -288,11 +293,13 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
log(error_msg)
return False, error_msg
log(f"下载文件耗时: {time.time() - downloadBeginTime}秒 - {objFiles['localPath']}")
# 下载成功之后要修改文件之间的关联路径
if objFiles["localPath"].endswith(".obj"):
beginChangeTime = time.time()
objsLocal = objFiles["localPath"]
changeObjFile(objFiles["localPath"], f"{orderId}_{pid}.mtl")
log(f"修改obj关联耗时: {time.time() - beginChangeTime}秒 - {objFiles['localPath']}")
if objFiles["localPath"].endswith(".mtl"):
changeMtlFile(objFiles["localPath"], f"{orderId}_{pid}Tex1.jpg")
@ -313,18 +320,56 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine @@ -313,18 +320,56 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
# 获取 small_machine_transform.py 的绝对路径
script_dir = os.path.dirname(os.path.abspath(__file__))
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)
# 使用 shlex.quote 来安全地转义路径和参数
transform_script_path_quoted = shlex.quote(transform_script_path)
objsLocal_quoted = shlex.quote(objsLocal)
homo_matrix_quoted = shlex.quote(homo_matrix_json)
error = os.system(f"{blender_bin_path} -b -P {transform_script_path_quoted} -- --objPathName={objsLocal_quoted} --trans={homo_matrix_quoted}")
if error != 0:
error_msg = f"调用blender 执行 python 文件失败, error={error}, fileName={fileName}"
# 构建命令参数列表(使用列表形式,避免 shell 转义问题)
cmd = [
blender_bin_path,
'--background',
'--python',
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)
return False, error_msg
log(f"调用blender 执行 python 文件成功, error={error}, fileName={fileName}")
timeEnd = time.time()
log(f"转换数据时间: 耗时{timeEnd - timeBegin}秒 - {objsLocal}")
@ -337,7 +382,7 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine @@ -337,7 +382,7 @@ def _process_single_item(v, arrDownloadPath, dirNewName, dirPath, isSmallMachine
# json文件进行下载对应的数据 和转换数据,传递目录路径
def downloadDataByOssAndTransformSave(dirNewName,isSmallMachine=False, max_workers=10):
def downloadDataByOssAndTransformSave(dirNewName, isSmallMachine=False, max_workers=1):
listData = getJsonData(dirNewName)
if not listData:
log(f"获取数据失败, dirNewName={dirNewName}")
@ -405,14 +450,35 @@ def downloadDataByOssAndTransformSave(dirNewName,isSmallMachine=False, max_worke @@ -405,14 +450,35 @@ def downloadDataByOssAndTransformSave(dirNewName,isSmallMachine=False, max_worke
def findBpyModule():
# 返回 Blender 可执行文件路径(macOS)
# 返回 Blender 可执行文件路径
blender_bin_path = '/Applications/Blender.app/Contents/MacOS/Blender'
# 判断当前是 windows 还是 macOS
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'
log(f"警告: 未找到 Blender 可执行文件,使用默认路径: {blender_bin_path}")
else:
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
Loading…
Cancel
Save