import requests,time import os,platform import shutil import json import shlex 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': url = 'http://mp.api.dev.com' elif ENV == 'prod': url = 'https://mp.api.suwa3d.com' #根据打印ID 获取下载目录 def getDownloadDirByPrintId(printIds): #调用接口获取下载的路径 api_url = f"{url}/api/printOrder/getInfoByPrintIds" res = requests.post(api_url,json={"print_ids":printIds}) res = res.json() print(f"根据打印ID 获取下载目录, res={res}") if res["code"] == 1000: return res["data"] else: return False #根据批次id,进行切片中的状态变更 def requestApiToUpdateSliceStatus(versionId,downloadCounts): api_url = f"{url}/api/printTypeSettingOrder/updateBatchSliceing?batch_id={versionId}&download_counts="+str(downloadCounts) log(f'发起状态变更请求url={api_url}, versionId={versionId}') try: # 添加超时参数,防止请求时间过长 res = requests.post(api_url, timeout=60) # 60秒超时 if res.status_code != 200: log(f'状态变更请求失败, res={res.text}') return False log(f'状态变更请求成功, res={res.text}') return True except requests.exceptions.Timeout: log(f'状态变更请求超时, url={api_url}') return False except requests.exceptions.RequestException as e: log(f'状态变更请求异常, error={str(e)}') return False #判断是否上传了 JSON 文件 def checkJsonFileExists(versionId): log(f"检测文件和图片是否存在, versionId={versionId}") jsonFilePath = f'batchPrint/{versionId}/{versionId}.json' #判断oss 上是否存在 jpgFilePath = f'batchPrint/{versionId}/{versionId}.jpg' if not ossClient().object_exists(jsonFilePath): log(f'JSON文件不存在: {jsonFilePath}') return False,False if not ossClient().object_exists(jpgFilePath): log(f'JPG文件不存在: {jpgFilePath}') return False,False log(f"文件和图片检测成功,存在, versionId={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}" res = requests.get(url1) res = res.json() log(f"获取批次信息和打印机信息, url={url1}, res={res}") if res["code"] == 1000: return res["data"] else: return False #下载文件,读取文件内容,并且文件迁移至正确的目录里,不再放在临时目录里 def downloadJsonAndJpgFileAndMoveToCorrectDir(versionId,currentDir): jsonFilePath,jpgFilePath = checkJsonFileExists(versionId) if jsonFilePath == False or jpgFilePath == False: return False,False #将文件下载到临时目录,待会要判断是否什么类型机型 tempDir = os.path.join(currentDir, 'batchPrint', 'temp', versionId) if not os.path.exists(tempDir): os.makedirs(tempDir) localFilePath = os.path.join(tempDir, f'{versionId}.json') # 使用带完整性校验的下载方法下载JSON文件 ok = download_file_with_check(jsonFilePath, localFilePath) if not ok: log(f"JSON 文件下载失败或不完整, versionId={versionId}") return False,False #下载JPG文件 localJpgFilePath = os.path.join(tempDir, f'{versionId}.jpg') ok = download_file_with_check(jpgFilePath, localJpgFilePath) if not ok: log(f"JPG 文件下载失败或不完整, versionId={versionId}") return False,False #根据批次ID,获取批次信息和打印机信息 batchMachineInfo = getBatchInfo(versionId) if not batchMachineInfo: log(f"获取批次信息和打印机信息失败, versionId={versionId}") return False,False # "batch_info": batchInfo, # "print_machine_info": printMachineInfo, machineInfo = batchMachineInfo["print_machine_info"] print(f"machineInfo={machineInfo['id']}") dirNewName = "" if str(machineInfo["machine_type"]) == '1': dirNewName = os.path.join(currentDir, 'batchPrint', versionId + '_small_No'+str(machineInfo['id'])) else: dirNewName = os.path.join(currentDir, 'batchPrint', versionId + '_big_No'+str(machineInfo['id'])) #判断目录是否存在,存在就删除 if os.path.exists(dirNewName): shutil.rmtree(dirNewName) #创建目录 os.makedirs(dirNewName) #创建json子目录 jsonSubDir = os.path.join(dirNewName, 'json') 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')) #检测文件是否移动成功 if not os.path.exists(os.path.join(jsonSubDir, '3DPrintLayout.json')): log(f"JSON文件不存在, versionId={versionId}") return False,False if not os.path.exists(os.path.join(jsonSubDir, f'{versionId}.jpg')): log(f"JPG文件不存在, versionId={versionId}") return False,False log(f"文件移动成功, versionId={versionId}") #返回目录路径 return dirNewName,machineInfo #整合json文件,读取对应的数据,返回数据结构 def getJsonData(dirNewName): jsonFilePath = os.path.join(dirNewName, 'json', '3DPrintLayout.json') if not os.path.exists(jsonFilePath): log(f"JSON文件不存在, dirNewName={dirNewName}") return False #读取JSON文件内容 jsonData = readJsonFile(jsonFilePath) #读取models models = jsonData.get('models', []) if not models: log(f"models不存在, dirNewName={dirNewName}") return False listData = [] #遍历models for model in models: file_name = model.get('file_name', '') #分割数据 arrFileName = file_name.split('_') orderId = arrFileName[0] pid = arrFileName[1] printId = arrFileName[2].replace("P", "") size = arrFileName[3] counts = arrFileName[4].replace("x", "").replace(".obj", "") #检测这些数据 if not orderId or not pid or not printId or not size or not counts: log(f"数据不完整, orderId={orderId}, pid={pid}, printId={printId}, size={size}, counts={counts}") return False #创建数据结构 modelInfo = { "orderId": orderId, "printId": printId, "pid": pid, "size": size, "counts": counts, "file_name": file_name, } listData.append(modelInfo) #检测数据长度 if len(listData) == 0: log(f"数据长度为0, dirNewName={dirNewName}") return False #返回数据结构 return listData # 读取 json 文件,获取 homo_matrix 数据,根据 file_name 获取 homo_matrix 数据 def getHomoMatrixByFileName(dirNewName,fileName): jsonFilePath = os.path.join(dirNewName, 'json', '3DPrintLayout.json') if not os.path.exists(jsonFilePath): log(f"JSON文件不存在, dirNewName={dirNewName}") return False #读取JSON文件内容 jsonData = readJsonFile(jsonFilePath) if not jsonData: log(f"读取JSON文件内容失败, dirNewName={dirNewName}") return False for model in jsonData["models"]: log(f"jsonData={model['file_name']} == {fileName}") if model["file_name"] == fileName: log(f"model={model['transform']}") return model["transform"]["homo_matrix"] return False #json文件进行下载对应的数据 和转换数据,传递目录路径 def downloadDataByOssAndTransformSave(dirNewName,isSmallMachine=False): listData = getJsonData(dirNewName) if not listData: log(f"获取数据失败, dirNewName={dirNewName}") return False #遍历数据 arrPrintId = [] arrPrintDataInfo = [] for modelInfo in listData: arrPrintId.append(modelInfo.get('printId')) arrPrintDataInfo.append({ "printId": modelInfo.get('printId'), "file_name": modelInfo.get('file_name'), }) #调用接口获取下载的路径 arrDownloadPath = getDownloadDirByPrintId(arrPrintId) if not arrDownloadPath: log(f"获取下载路径失败, arrPrintId={arrPrintId}") return False dirPath = os.path.join(dirNewName, 'data') if not os.path.exists(dirPath): os.makedirs(dirPath) #遍历数据 for v in listData: info = {} for tempv in arrDownloadPath: #print(f"tempv={tempv['print_order_id']} == {v['printId']}") if str(tempv["print_order_id"]) == str(v["printId"]): info = tempv break #print(f"info={info}") filePath = info["path"] pid = info["pid"] orderId = info["order_id"] printId = info["print_order_id"] size = info["real_size"] counts = info["quantity"] fileName = v["file_name"] #判断文件是否存在 ossJpgFilePath = f"{filePath}/printId_{printId}Tex1.jpg" if not checkFileExists(ossJpgFilePath): ossJpgFilePath = f"{filePath}/{pid}Tex1.jpg" localJpgName = os.path.join(f"{orderId}_{pid}Tex1.jpg") loaclMtlName = os.path.join(f"{orderId}_{pid}.mtl") localObjName = fileName arrDownloadFiles = [ {"ossPath": ossJpgFilePath, "localPath": os.path.join(dirPath, localJpgName)}, {"ossPath": f"{filePath}/{pid}.obj", "localPath": os.path.join(dirPath, localObjName)}, {"ossPath": f"{filePath}/{pid}.mtl", "localPath": os.path.join(dirPath, loaclMtlName)}, ] #遍历下载文件 beginTime = time.time() objsLocal = "" for objFiles in arrDownloadFiles: if "F" in fileName: #判断 mtl 和 jpg 文件是否存在,存在就不在下载了 if "mtl" in objFiles["localPath"] or "jpg" in objFiles["localPath"]: if os.path.exists(objFiles["localPath"]): continue downloadOk = download_file_with_check(objFiles["ossPath"], objFiles["localPath"]) if not downloadOk: log(f"下载文件失败, ossPath={objFiles["ossPath"]}, localPath={objFiles["localPath"]}") return False #下载成功之后要修改文件之间的关联路径 if objFiles["localPath"].endswith(".obj"): objsLocal = objFiles["localPath"] changeObjFile(objFiles["localPath"], f"{orderId}_{pid}.mtl") if objFiles["localPath"].endswith(".mtl"): changeMtlFile(objFiles["localPath"], f"{orderId}_{pid}Tex1.jpg") endTime = time.time() log(f"下载文件和修改文件之间的关联路径 : 耗时{endTime - beginTime}秒") #如果是小机台,则要转换数据 if not isSmallMachine: continue timeBegin = time.time() homo_matrix = getHomoMatrixByFileName(dirNewName, localObjName) if not homo_matrix: log(f"获取homo_matrix失败, dirNewName={dirNewName}, objsLocal={objsLocal}") return False # transform_save(objFiles["localPath"], homo_matrix) #通过blender 调用执行 python 文件 blender_bin_path = findBpyModule() # 获取 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 = 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: log(f"调用blender 执行 python 文件失败, error={error}") return False log(f"调用blender 执行 python 文件成功, error={error}") timeEnd = time.time() log(f"转换数据时间: 耗时{timeEnd - timeBegin}秒 - {objsLocal}") return True def findBpyModule(): # 返回 Blender 可执行文件路径(macOS) blender_bin_path = '/Applications/Blender.app/Contents/MacOS/Blender' # 判断当前是 windows 还是 macOS if platform.system() == 'Windows': blender_bin_path = 'C:\\Program Files\\Blender Foundation\\Blender 4.4\\blender.exe' else: blender_bin_path = '/Applications/Blender.app/Contents/MacOS/Blender' return blender_bin_path