@ -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