1 changed files with 402 additions and 0 deletions
@ -0,0 +1,402 @@
@@ -0,0 +1,402 @@
|
||||
import os, sys, time, bpy, math, requests, bmesh, json, shutil,oss2,chardet |
||||
from PIL import Image |
||||
import platform,redis |
||||
if platform.system() == 'Windows': |
||||
sys.path.append('e:\\libs\\') |
||||
#sys.path.append('libs') |
||||
else: |
||||
sys.path.append('/data/deploy/make3d/make2/libs/') |
||||
import config, libs, libs_db,main_service_db,common,foot_mark_seam,libs_db_gpu |
||||
|
||||
def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False): |
||||
"""Returns a transformed, triangulated copy of the mesh""" |
||||
assert obj.type == 'MESH' |
||||
if apply_modifiers and obj.modifiers: |
||||
import bpy |
||||
depsgraph = bpy.context.evaluated_depsgraph_get() |
||||
obj_eval = obj.evaluated_get(depsgraph) |
||||
me = obj_eval.to_mesh() |
||||
bm = bmesh.new() |
||||
bm.from_mesh(me) |
||||
obj_eval.to_mesh_clear() |
||||
else: |
||||
me = obj.data |
||||
if obj.mode == 'EDIT': |
||||
bm_orig = bmesh.from_edit_mesh(me) |
||||
bm = bm_orig.copy() |
||||
else: |
||||
bm = bmesh.new() |
||||
bm.from_mesh(me) |
||||
if transform: |
||||
matrix = obj.matrix_world.copy() |
||||
if not matrix.is_identity: |
||||
bm.transform(matrix) |
||||
matrix.translation.zero() |
||||
if not matrix.is_identity: |
||||
bm.normal_update() |
||||
if triangulate: |
||||
bmesh.ops.triangulate(bm, faces=bm.faces) |
||||
return bm |
||||
|
||||
def find_pid_objname(pid): |
||||
print(bpy.data.objects) |
||||
for obj in bpy.data.objects: |
||||
print(obj) |
||||
if obj.name.startswith(str(pid)): |
||||
return obj.name |
||||
|
||||
|
||||
def down_obj_from_oss(workdir, pid, action): |
||||
if os.path.exists(os.path.join(workdir, action, pid)): |
||||
#print(f'目录{os.path.join(workdir, action, pid)}已存在,跳过') |
||||
#删除目录 |
||||
shutil.rmtree(os.path.join(workdir, action, pid)) |
||||
|
||||
os.makedirs(os.path.join(workdir, action, pid)) |
||||
|
||||
jpgsTemp = f'objs/print/{pid}/base/model/{pid}Tex1.jpg' |
||||
mtlTemp = f'objs/print/{pid}/base/model/{pid}.mtl' |
||||
objTemp = f'objs/print/{pid}/base/model/{pid}.obj' |
||||
|
||||
# 根据前缀获取文件列表 |
||||
prefix = f'objs/print/{pid}/base/model/' |
||||
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) |
||||
print('正在下载:', prefix) |
||||
obj_filename = "" |
||||
for file in filelist: |
||||
|
||||
|
||||
filename = file.key.split('/')[-1] |
||||
# if filename.endswith(".exr"): |
||||
# continue |
||||
|
||||
# if filename.endswith("_1.jpg") or filename.endswith("_8.jpg"): |
||||
# continue |
||||
|
||||
|
||||
if file.key != jpgsTemp and file.key != mtlTemp and file.key != objTemp: |
||||
continue |
||||
|
||||
if filename.endswith('.obj'): |
||||
obj_filename = filename |
||||
print('正在下载:', file.key,filename) |
||||
localfile = os.path.join(workdir, action, pid, filename) |
||||
config.oss_bucket.get_object_to_file(file.key, localfile) |
||||
|
||||
return obj_filename |
||||
|
||||
def checkDownloadFile(pid): |
||||
mtlfilePath = os.path.join(config.workdir, "print", pid, f"{pid}.mtl") |
||||
# objfilePath = os.path.join(config.workdir, "print", pid, f"{pid}.obj") |
||||
|
||||
# 读取文件内容(自动检测编码) |
||||
def read_file_auto_encoding(path): |
||||
with open(path, 'rb') as f: |
||||
raw_data = f.read() |
||||
encoding = chardet.detect(raw_data)['encoding'] or 'utf-8' |
||||
return raw_data.decode(encoding, errors='ignore') |
||||
|
||||
mtlContent = read_file_auto_encoding(mtlfilePath) |
||||
|
||||
if mtlContent == "": |
||||
print(f'{pid}的mtl文件检测出现问题了, 内容为空') |
||||
return False |
||||
|
||||
newmtlCount = mtlContent.count('newmtl') |
||||
if newmtlCount > 1: |
||||
print(f'{pid}的mtl文件检测出现问题了, 包含多个newmtl') |
||||
return False |
||||
|
||||
if f'{pid}Tex1.jpg' not in mtlContent: |
||||
print(f'{pid}的mtl文件检测出现问题了, 没有找到{pid}Tex1.jpg') |
||||
return False |
||||
|
||||
# # 检测obj文件 |
||||
# isContain = False |
||||
|
||||
# objContent = read_file_auto_encoding(objfilePath) |
||||
|
||||
|
||||
# for line in objContent.splitlines(): |
||||
# if line.startswith(f'o {pid}'): |
||||
# isContain = True |
||||
# print(f'{pid}的obj文件检测没问题') |
||||
# break |
||||
|
||||
# if not isContain: |
||||
# print(f'{pid}的obj文件检测出现问题了, 没有找到o {pid} 的行') |
||||
# return False |
||||
|
||||
return True |
||||
|
||||
#加载obj文件 |
||||
def reload_obj(pid,order_ids): |
||||
for order_id in order_ids: |
||||
print(order_id) |
||||
#url 请求 |
||||
url = f"https://mp.api.suwa3d.com/api/printOrder/infoByOrderId?id={order_id}" |
||||
response = requests.get(url) |
||||
response = response.json() |
||||
if response['code'] != 1000: |
||||
continue |
||||
|
||||
#获取打印尺寸 |
||||
printdata = response['data']['data'] |
||||
for key,value in printdata.items(): |
||||
#去掉cm |
||||
key = key.replace('cm','') |
||||
key = float(key)*10 |
||||
print(key) |
||||
print(f'执行脚本--- "python D:/make2/tools/cal_weight.py print {pid} {key} {order_id}"') |
||||
#发起计算请求 |
||||
os.system(f"python D:/make2/tools/cal_weight.py print {pid} {key} {order_id}") |
||||
#计算克重信息 |
||||
|
||||
|
||||
obj_filename = os.path.join(config.workdir,'print',pid,f'{pid}.obj') |
||||
bpy.ops.wm.read_homefile() |
||||
bpy.ops.object.delete(use_global=False, confirm=False) |
||||
bpy.ops.import_scene.obj(filepath=obj_filename) |
||||
bpy.context.scene.unit_settings.scale_length = 1 |
||||
bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' |
||||
bpy.context.scene.unit_settings.mass_unit = 'GRAMS' |
||||
|
||||
obj = bpy.context.selected_objects[0] |
||||
bpy.context.view_layer.objects.active = obj |
||||
obj.select_set(True) |
||||
|
||||
def get_p3d_info(pid): |
||||
url = "https://mp.api.suwa3d.com/api/customerP3dLog/info?id="+pid |
||||
res = requests.get(url) |
||||
res = res.json() |
||||
if res["code"] == 1000: |
||||
return res["data"]["guid"] |
||||
else: |
||||
return 0 |
||||
|
||||
|
||||
def base_fix(pid,order_ids): |
||||
# 统一blender环境 |
||||
print("BBBBBBBBBBBBB") |
||||
reload_obj(pid,order_ids) |
||||
# 统一模型方向、位置、大小... |
||||
pid_objname = find_pid_objname(pid) |
||||
print("AAAAAAAAAAAAA",pid_objname) |
||||
obj = bpy.context.selected_objects[0] #bpy.data.objects[pid_objname] |
||||
obj.rotation_euler = (0, 0, 0) |
||||
# 检查当前模型是否正确使用 z 轴作为高度轴 |
||||
if obj.dimensions.z < obj.dimensions.y: |
||||
# 如果 z 轴比 y 轴小,说明高度错误,应该旋转调整模型 |
||||
print(f"警告:模型的高度轴(z)似乎不正确,使用 y 轴作为高度。正在修正模型方向...") |
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) # 应用当前变换 |
||||
obj.rotation_euler[0] = math.radians(90) # 旋转模型 90 度,交换 z 和 y 轴 |
||||
bpy.ops.object.transform_apply(rotation=True) # 应用旋转 |
||||
|
||||
|
||||
|
||||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') |
||||
bpy.context.object.location[0] = 0 |
||||
bpy.context.object.location[1] = 0 |
||||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型基础校正完成') |
||||
|
||||
|
||||
def export_and_update_glbs(pid,order_ids): |
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始导出并上传审核模型和3D相册模型glb文件...') |
||||
start_time = time.time() |
||||
#headcount = libs.getHeadCount(pid) |
||||
headcount = 1 |
||||
pid_objname = find_pid_objname(pid) |
||||
obj = bpy.context.selected_objects[0] |
||||
obj.select_set(True) |
||||
|
||||
model_info = {} |
||||
model_info['headcount'] = headcount |
||||
model_info['faces'] = round(len(obj.data.polygons) / 10000) |
||||
model_info['height'] = round(obj.dimensions.y * 100) |
||||
|
||||
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}.blend')) |
||||
|
||||
# 统一缩放到9cm标准尺寸 |
||||
scale = 90 / obj.dimensions.y |
||||
obj.scale = (scale, scale, scale) |
||||
bpy.ops.object.transform_apply(scale=True) |
||||
|
||||
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}-9cm.blend')) |
||||
|
||||
bm = bmesh_copy_from_object(obj) |
||||
model_info['volume'] = round(bm.calc_volume(), 2) |
||||
model_info['weight'] = round(model_info['volume'] * 1.226, 2) |
||||
print(f'{pid}的模型数据:{model_info}') |
||||
|
||||
#res = requests.get(f'{config.urls["upload_model_info_url"]}?pid={pid}&headcount={headcount}&faces={model_info["faces"]}&volume={model_info["volume"]}&weight={model_info["weight"]}&height={model_info["height"]}') |
||||
#print('上传模型数据:', res.text) |
||||
# with open(os.path.join(config.sharedir, 'model_info', f'{pid}.json'), 'w') as f: |
||||
# json.dump(model_info, f) |
||||
# f.close() |
||||
|
||||
# 先生成审核模型 |
||||
faces_dest = 500000 * headcount |
||||
# 减面 |
||||
faces_current = len(obj.data.polygons) |
||||
print(f'当前面数:{faces_current},目标面数:{faces_dest}') |
||||
|
||||
bpy.ops.object.modifier_add(type='DECIMATE') |
||||
bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current |
||||
bpy.ops.object.modifier_apply(modifier="Decimate") |
||||
|
||||
glb_filename = os.path.join(config.workdir, 'print',pid, f'{pid}.glb') |
||||
bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) |
||||
|
||||
# glb_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.glb') |
||||
# bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) |
||||
|
||||
# 再生成数字模型 |
||||
faces_dest = 120000 * headcount |
||||
|
||||
# 减面 |
||||
faces_current = len(obj.data.polygons) |
||||
print(f'当前面数:{faces_current},目标面数:{faces_dest}') |
||||
|
||||
bpy.ops.object.modifier_add(type='DECIMATE') |
||||
bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current |
||||
bpy.ops.object.modifier_apply(modifier="Decimate") |
||||
|
||||
glb_filename = os.path.join(config.workdir,'print',pid,f'{pid}-3d.glb') |
||||
bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) |
||||
|
||||
# os.system(f'gltfpack -c -i {os.path.join(config.workdir, "print",pid, f"{pid}.glb")} -o {os.path.join(config.workdir, "print",pid, f"{pid}-pack.glb")}') |
||||
|
||||
config.oss_bucket.put_object_from_file(f'glbs/auto/{pid}.glb', os.path.join(config.workdir, 'print',pid, f'{pid}-3d.glb')) |
||||
config.oss_bucket.put_object_from_file(f'glbs/print/{pid}.glb', os.path.join(config.workdir, 'print',pid, f'{pid}-3d.glb')) |
||||
config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', os.path.join(config.workdir,'print',pid, f'{pid}-3d.glb')) |
||||
|
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} glb文件导出并上传完成,共费时{libs.diff_time(start_time)}') |
||||
|
||||
#移除文件夹 |
||||
#shutil.rmtree(os.path.join(config.workdir,'print',pid)) |
||||
|
||||
#更新该笔订单的状态为 3000 |
||||
# order_ids 逗号隔开 |
||||
print(order_ids) |
||||
order_ids = ",".join(map(str,order_ids)) |
||||
strRequest = f"https://shop.api.suwa3d.com/api/printOrder/updateExternalOrderStatusV2?pid={pid}&order_ids={order_ids}" |
||||
print(strRequest) |
||||
res = requests.post("https://shop.api.suwa3d.com/api/printOrder/updateExternalOrderStatusV2", data={'pid': pid,"order_ids":order_ids}) |
||||
print(res.text) |
||||
# # 记录日志文件 |
||||
# logPath = os.path.join(config.workdir,"log",'external_order_glbV2',pid,f'{order_ids}.log') |
||||
# os.makedirs(logPath, exist_ok=True) |
||||
# # 创建并且写入日志 |
||||
# with open(logPath, 'w+') as f: |
||||
# f.write(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 订单状态更新完成,共费时{libs.diff_time(start_time)}\n') |
||||
# f.write(f'{res.text}\n') |
||||
# f.write(f'{strRequest}\n') |
||||
|
||||
|
||||
|
||||
#执行获取obj缩略图 |
||||
print("执行获取obj全身缩略图脚本") |
||||
os.system(f'python d:\\make2\\tools\pic_for_obj\image_rander_small.py -pid {pid} -i D://print/{pid} -o D://print/{pid}') |
||||
print("判断获取封面图是否存在0") |
||||
#判断文件是否存在,存在则上传到oss, 更新数据库内容 |
||||
if os.path.exists(f'D://print/{pid}/{pid}_pic.png'): |
||||
#获取拍照订单的信息,从中得到拍照订单的信息 |
||||
uuid = get_p3d_info(str(pid)) |
||||
print("判断获取封面图是否存在1") |
||||
if uuid != 0 and uuid != None and uuid != "": |
||||
print("uuid",uuid) |
||||
config.oss_bucket_3d_view.put_object_from_file(f'{uuid}/3d_view.png', f'D://print/{pid}/{pid}_pic.png') |
||||
|
||||
# 读取队列的信息,获取内容快照, 按时间戳命名 文件 |
||||
r = create_redis_connection() |
||||
info = r.lrange('model:printOrder', 0, -1) |
||||
print(info) |
||||
# 将info写入到文件中 |
||||
with open(os.path.join(config.workdir,'log','external_order_glbV2',f'{pid}_{time.time()}.log'), 'w') as f: |
||||
f.write(str(info)) |
||||
|
||||
def createGlb(pid,order_ids): |
||||
|
||||
#下载obj文件 |
||||
down_obj_from_oss(config.workdir, pid, "print") |
||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 下载obj文件完成...') |
||||
#检测mtl 文件是否正常, |
||||
isNormal = checkDownloadFile(pid) |
||||
if isNormal == False: |
||||
print(f'{pid}的mtl文件检测出现问题,不执行该笔任务,调用取消的接口') |
||||
for order_id in order_ids: |
||||
url = f"https://mp.api.suwa3d.com/api/order/cancelExternalOrder?order_id={order_id}&typename=system" |
||||
response = requests.get(url) |
||||
response = response.json() |
||||
print(response) |
||||
if response['code'] != 1000: |
||||
continue |
||||
print(f'取消订单{order_id}成功') |
||||
return |
||||
|
||||
base_fix(pid,order_ids) |
||||
export_and_update_glbs(pid,order_ids) |
||||
#移除文件夹 |
||||
# try: |
||||
# shutil.rmtree(os.path.join(config.workdir,'print',pid)) |
||||
# except: |
||||
# pass |
||||
|
||||
|
||||
def create_redis_connection(): |
||||
"""创建 Redis 连接,若连接失败则重试""" |
||||
while True: |
||||
try: |
||||
r = redis.Redis(host="106.14.158.208",password="kcV2000",port=6379,db=6) |
||||
# 尝试进行一次操作,检查连接是否有效 |
||||
r.ping() # ping 操作是一个简单的连接测试 |
||||
print("Redis连接成功!") |
||||
return r |
||||
except ConnectionError: |
||||
print("Redis连接失败,正在重试...") |
||||
time.sleep(5) |
||||
|
||||
if __name__ == '__main__': |
||||
# pid = "286049" |
||||
# order_ids = [863038] |
||||
# createGlb(str(pid),order_ids) |
||||
arrArgs = sys.argv |
||||
if len(arrArgs) == 2: |
||||
pid = arrArgs[1] |
||||
#判断是否是数字 |
||||
if pid.isdigit(): |
||||
print('请输入正确的pid') |
||||
createGlb(str(pid)) |
||||
|
||||
else: |
||||
r = create_redis_connection() |
||||
while True: |
||||
try: |
||||
if r.llen('model:3dglbV2') == 0: |
||||
print('队列为空,等待10秒') |
||||
time.sleep(10) |
||||
continue |
||||
|
||||
info = r.lpop('model:3dglbV2') |
||||
if info is None: |
||||
print('队列为空,等待10秒') |
||||
time.sleep(10) |
||||
|
||||
|
||||
info = json.loads(info) |
||||
print(info) |
||||
pid = info['pid'] |
||||
order_ids = info['order_id'] |
||||
|
||||
# if not pid.isdigit(): |
||||
# continue |
||||
|
||||
createGlb(str(pid),order_ids) |
||||
except Exception as e: |
||||
print(f'错误:{e}') |
||||
time.sleep(10) |
||||
r = create_redis_connection() |
||||
continue |
||||
|
||||
|
||||
Loading…
Reference in new issue