diff --git a/blender/gray_fill_dm_code.py b/blender/gray_fill_dm_code.py new file mode 100644 index 0000000..e028853 --- /dev/null +++ b/blender/gray_fill_dm_code.py @@ -0,0 +1,847 @@ +import os, sys, bpy, math, time, platform, cairosvg, ppf.datamatrix, shutil, requests, json, redis, oss2, cv2,qrcode +from retrying import retry +import subprocess +import multiprocessing +import random +import numpy as np +import matplotlib.pyplot as plt +from PIL import Image, ImageEnhance +from addon_utils import enable +import logging,atexit,platform +# if platform.system() == 'Windows': +sys.path.append('/home/acprint/code/libs/') +import common +logging.basicConfig(filename='foot_update_res.log', level=logging.ERROR) +enable('io_import_images_as_planes') +enable('eyek_addon') + + +#查询影棚ID +def getPSid(pid): + res = requests.get("https://mp.gray.api.suwa3d.com/api/customerP3dLog/photoStudio",params={"pid":pid}) + res = json.loads(res.text) + return str(res['data']) + + +def restart_current_process(new_command): + try: + # 保存新进程的命令 + command = new_command.split() + + # 启动新进程 + new_process = subprocess.Popen(command) + + # 打印新进程的PID + print(f"New process started with PID: {new_process.pid}") + + # 终止当前进程 + os._exit(0) + + except Exception as e: + print(f"An error occurred: {e}") + +#生成二维码图片 +def gen_data_matrix(print_id,pid, qr_path, size = 300): + psid = getPSid(pid) + if int(psid) == 0: + print("费工夫构建脚底二维码") + # if use_foot_type == "short_url": + #调用接口获取短网址信息 + short_url = get_short_url(print_id) + if short_url == False: + return + temp_foot_data = short_url + + #生成二维码 + qr = qrcode.QRCode( + version=1, + error_correction = qrcode.constants.ERROR_CORRECT_L, + box_size=10, + border=2, + ) + qr.add_data(temp_foot_data) + qr.make(fit=True) + img = qr.make_image(fill_color="black",back_color="white").resize((size,size)) + img.save(qr_path) + else: + #正常生成不是常规的二维码 + print("排除费工夫正常构建二维码") + svg = ppf.datamatrix.DataMatrix(f'p{print_id}').svg() + cairosvg.svg2png(bytestring=svg, write_to=qr_path, output_width=size, output_height=size, background_color='white') + +def active_object(obj): + bpy.context.view_layer.objects.active = obj + obj.select_set(True) +#下载oss的文件 +@retry(stop_max_attempt_number=10, wait_fixed=2000) +def down_obj_fromoss(pid, print_type=1, order_id=None): + # print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品 + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始下载模型,若网络异常将每间隔2秒重试10次...') + if not order_id is None: + path = os.path.join(workdir, f'{pid}_{order_id}') + else: + path = os.path.join(workdir, pid) + if os.path.exists(path): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 已存在模型文件,删除后重新下载') + shutil.rmtree(path, ignore_errors=True) + os.makedirs(path) + + prefix = f'objs/print/{pid}/' + #根据order_id 获取文件在 oss 上的存储路径 + if order_id is not None: + tempURL = "https://mp.gray.api.suwa3d.com/api/order/getOssSuffixByOrderId?orderId="+str(order_id) + res = requests.get(tempURL) + resCode = res.json()['code'] + if int(resCode) != 1000: + return -1 + print(res.text) + if res.json()['data'] != "": + prefix = f'objs/print/{pid}/{res.json()["data"]}/{str(order_id)}/' + + # 下载分2种情况,一种是第一次打印,下载标准{pid}.obj,{pid}.mtl,{pid}Tex1.jpg,另一种是重打或加打,obj文件名可以从oss上任意获取一个,但是mtl和jpg文件名是固定的 + res = oss_client.get_object_to_file(f'{prefix}/{pid}.mtl', os.path.join(path, f'{pid}.mtl')) + last_modified = oss_client.get_object_meta(f"{prefix}/{pid}.mtl").last_modified + print(f'mtl文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') + print(f'下载文件:{prefix}/{pid}.mtl,状态:{res.status}') + if oss_client.object_exists(f'{prefix}/{pid}Tex1.jpg'): + res = oss_client.get_object_to_file(f'{prefix}/{pid}Tex1.jpg', os.path.join(path, f'{pid}Tex1.jpg')) + last_modified = oss_client.get_object_meta(f"{prefix}/{pid}Tex1.jpg").last_modified + print(f'jpg文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') + print(f'下载文件:{prefix}/{pid}Tex1.jpg,状态:{res.status}') + else: + res = oss_client.get_object_to_file(f'{prefix}/{pid}.jpg', os.path.join(path, f'{pid}Tex1.jpg')) + last_modified = oss_client.get_object_meta(f"{prefix}/{pid}.jpg").last_modified + print(f'jpg文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') + print(f'下载文件:{prefix}/{pid}.jpg,状态:{res.status}') + if oss_client.object_exists(f'{prefix}/{pid}.obj'): + res = oss_client.get_object_to_file(f'{prefix}/{pid}.obj', os.path.join(path, f'{pid}.obj')) + last_modified = oss_client.get_object_meta(f"{prefix}/{pid}.obj").last_modified + print(f'obj文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') + print(f'下载文件:{prefix}/{pid}.obj,状态:{res.status}') + else: + prefix = f'{prefix}' + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == '': continue + if filename.endswith(f'.obj'): + res = oss_client.get_object_to_file(file.key, os.path.join(path, f'{pid}.obj')) + last_modified = oss_client.get_object_meta(file.key).last_modified + print(f'obj文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') + print(f'下载文件:{file.key},状态:{res.status}') + break +#查找obj +def find_obj(pid, order_id): + find = False + if os.path.exists(os.path.join(sourceFilePath, f'{pid}_{order_id}', f'{pid}.obj')): + + if not os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.obj')): + #复制文件到 指定目录 + shutil.copytree(os.path.join(sourceFilePath, f'{pid}_{order_id}'), os.path.join(workdir,f'{pid}_{order_id}')) + + # shutil.copy(f"/data/datasets/print/{pid}_{order_id}/{pid}.obj", os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.obj')) + # shutil.copy(f"/data/datasets/print/{pid}_{order_id}/{pid}.mtl", os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl')) + # shutil.copy(f"/data/datasets/print/{pid}_{order_id}/{pid}.Tex1.jpg", os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg')) + else: + print('没有找到obj模型文件,开始下载') + down_obj_fromoss(pid, order_id=order_id) + if os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg')): + shutil.move(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg'), os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg')) + with open(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl'), 'r') as f: + lines = f.readlines() + lines = [line.replace(f'map_Kd {pid}.jpg', f'map_Kd {pid}Tex1.jpg') for line in lines] + with open(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl'), 'w') as f: + f.writelines(lines) + filelist = os.listdir(os.path.join(workdir, f'{pid}_{order_id}')) + for filename in filelist: + if f'{pid}.obj' in filename: + find = True + #进行图片分辨率的调整 + # get_printsize_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/info' + # res = requests.get(f'{get_printsize_url}?id={order_id}') + # print('获取订单信息:', res.text) + # if res.status_code == 200: + # headcount = res.json()['data']['headcount'] + # for f in res.json()['data']['fileList']: + # if "undefined" in f: + # continue + # try: + # height = float(f.split('_')[-2][:-2]) * 10 + # except Exception as e: + # continue + + # if headcount == 1 and height <= 80: + # pixs = 4096 + # if height <= 5: + # pixs = 1024 + # print(f"图片分辨率进行处理{pixs}") + # imagePiex(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg'),pixs) + + return filename + print('没有找到obj模型文件') + + + + + + + return '' + +def find_pid_objname(pid): + for obj in bpy.data.objects: + if obj.name.startswith(str(pid)): + return obj.name + +def ps_color_scale_adjustment(image, shadow=0, highlight=255, midtones=1): + ''' + 模拟 PS 的色阶调整; 0 <= Shadow < Highlight <= 255 + :param image: 传入的图片 + :param shadow: 黑场(0-Highlight) + :param highlight: 白场(Shadow-255) + :param midtones: 灰场(9.99-0.01) + :return: 图片 + ''' + if highlight > 255: + highlight = 255 + if shadow < 0: + shadow = 0 + if shadow >= highlight: + shadow = highlight - 2 + if midtones > 9.99: + midtones = 9.99 + if midtones < 0.01: + midtones = 0.01 + image = np.array(image, dtype=np.float16) + # 计算白场 黑场离差 + Diff = highlight - shadow + image = image - shadow + image[image < 0] = 0 + image = (image / Diff) ** (1 / midtones) * 255 + image[image > 255] = 255 + image = np.array(image, dtype=np.uint8) + + return image + + +def show_histogram(image, image_id, save_hist_dir, min_threshold, max_threshold): + ''' + 画出直方图展示 + :param image: 导入图片 + :param image_id: 图片id编号 + :param save_hist_dir: 保存路径 + :param min_threshold: 最小阈值 + :param max_threshold: 最大阈值 + :return: 原图image,和裁剪原图直方图高低阈值后的图片image_change + ''' + plt.rcParams['font.family'] = 'SimHei' + plt.rcParams['axes.unicode_minus'] = False + plt.hist(image.ravel(), 254, range=(2, 256), density=False) + plt.hist(image.ravel(), 96, range=(2, 50), density=False) # 放大 range(0, 50),bins值最好是range的两倍,显得更稀疏,便于对比 + plt.hist(image.ravel(), 110, range=(200, 255), density=False) # 放大 range(225, 255) + plt.annotate('thresh1=' + str(min_threshold), # 文本内容 + xy=(min_threshold, 0), # 箭头指向位置 # 阈值设定值! + xytext=(min_threshold, 500000), # 文本位置 # 阈值设定值! + arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头 + plt.annotate('thresh2=' + str(max_threshold), # 文本内容 + xy=(max_threshold, 0), # 箭头指向位置 # 阈值设定值! + xytext=(max_threshold, 500000), # 文本位置 # 阈值设定值! + arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头 + # 在y轴上绘制一条直线 + # plt.axhline(y=10000, color='r', linestyle='--', linewidth=0.5) + plt.title(str(image_id)) + # plt.show() + # 保存直方图 + save_hist_name = os.path.join(save_hist_dir, f'{image_id}_{min_threshold}&{max_threshold}.jpg') + plt.savefig(save_hist_name) + # 清空画布, 防止重叠展示 + plt.clf() + + +def low_find_histogram_range(image, target_frequency): + ''' + 循环查找在 target_frequency (y)频次限制下的直方图区间值(x) + :param image: 导入图片 + :param target_frequency: 直方图 y 频次限制条件 + :return: 直方图区间 x,和 该区间频次 y + ''' + # 计算灰度直方图 + hist, bins = np.histogram(image, bins=256, range=[0, 256]) + # 初始化区间和频次 + interval = 2 + frequency = hist[255] + while frequency < target_frequency: + # 更新区间和频次 + interval += 1 + # 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。 + frequency = hist[interval] if hist[interval] is not None else 0 + frequency += hist[interval] if hist[interval] is not None else 0 + # 如果频次接近10000则停止循环 + if target_frequency - 2000 <= frequency <= target_frequency + 1000: + break + + return interval, frequency + + +def high_find_histogram_range(image, target_frequency): + ''' + 循环查找在 target_frequency (y)频次限制下的直方图区间值(x) + :param image: 导入图片 + :param target_frequency: 直方图 y 频次限制条件 + :return: 直方图区间 x,和 该区间频次 y + ''' + # 计算灰度直方图 + hist, bins = np.histogram(image, bins=256, range=[0, 256]) + # 初始化区间和频次 + interval = 255 + frequency = hist[255] + while frequency < target_frequency: + # 更新区间和频次 + interval -= 1 + # 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。 + frequency = hist[interval] if hist[interval] is not None else 0 + frequency += hist[interval] if hist[interval] is not None else 0 + # 如果频次接近10000则停止循环 + if target_frequency - 2000 <= frequency <= target_frequency + 2000: + break + + return interval, frequency + +def reduce_sharpness(image, factor): + ''' + 使用PIL库减弱图像锐度 + :param image: 图像 + :param factor: 锐度因子,0表示最大程度减弱锐度,1表示原始图像 + :return: 减弱锐度后的图像 + ''' + # OpenCV 格式的图像转换为 PIL 的 Image 对象 + image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + pil_image = Image.fromarray(image_rgb) + enhancer = ImageEnhance.Sharpness(pil_image) + reduced_image = enhancer.enhance(factor) + # PIL 的 Image 对象转换为 OpenCV 的图像格式 + image_array = np.array(reduced_image) + sharpened_image = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR) + + return sharpened_image + +def sharpening_filter(image): + ''' + 锐化滤波器对图片进行锐化,增强图像中的边缘和细节 + :param image: 导入图片 + :return: 锐化后的图片 + ''' + sharp_kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) + sharpened_image = cv2.filter2D(image, -1, sharp_kernel) + return sharpened_image + +def find_last_x(image, slope_threshold = 1000): + x = [] + y = [] + hist, bins = np.histogram(image, bins=256, range=[0, 256]) + + #找到50以内的最高峰 + max_y = 0 + max_i = 5 + for i in range(5, 50): + if hist[i] > max_y: + max_y = hist[i] + max_i = i + print(f'50以内最高峰值y:{max_y},最高峰位置x:{max_i}') + + for i in range(2, max_i): + x.append(i) + y.append(hist[i]) + slopes = [abs(y[i + 1] - y[i]) for i in range(len(x) - 1)] + + current_interval = [] + max_interval = [] + max_x = {} + for i, slope in enumerate(slopes): + current_interval.append(slope) + if slope >= slope_threshold: + if len(current_interval) > len(max_interval): + max_interval = current_interval.copy() + max_x[x[i]] = slope + current_interval = [] + + print(max_x) + last_x = list(max_x)[-1] + last_y = max_x[last_x] + return last_x, last_y + +def find_last_high(image, slope_threshold = 2500): + x = [] + y = [] + hist, bins = np.histogram(image, bins=255, range=[2, 255]) + + #找到200以上的最高峰 + max_y = 0 + max_i = 254 + for i in range(220, 255): + if hist[i] > max_y: + max_y = hist[i] + max_i = i + print(f'200以上的最高峰值y:{max_y},最高峰位置x:{max_i}') + + for i in range(max_i, 255): + x.append(i) + y.append(hist[i]) + slopes = [abs(y[i + 1] - y[i]) for i in range(len(x) - 1)] + + current_interval = [] + max_interval = [] + max_x = {} + find = False + for i in range(len(slopes) - 1, -1, -1): + slope = slopes[i] + current_interval.append(slope) + if slope >= slope_threshold: + find = True + if len(current_interval) > len(max_interval): + max_interval = current_interval.copy() + max_x[x[i]] = slope + current_interval = [] + #如果没有找到200以上很平,而且高度小于5000,就按220位置削平 + if not find and hist[220] < 5000: + max_x[220] = hist[220] + + print(max_x) + if len(max_x) > 0: + last_x = list(max_x)[0] + last_y = max_x[last_x] + else: + print(f'找不到200以上曲线较平的区间,使用254作为最高峰') + last_x = 254 + last_y = hist[254] + return last_x, last_y + +def remove_gray_and_sharpening(jpg_path): + input_image = cv2.imread(jpg_path) + # low_x_thresh, low_y_frequency = low_find_histogram_range(input_image, low_y_limit) + low_x_thresh, low_y_frequency = find_last_x(input_image) + # high_x_thresh, high_y_frequency = high_find_histogram_range(input_image, high_y_limit) + high_x_thresh, high_y_frequency = find_last_high(input_image) + print(f"{low_x_thresh} 区间, {low_y_frequency} 频次") + print(f"{high_x_thresh} 区间, {high_y_frequency} 频次") + high_output_image = ps_color_scale_adjustment(input_image, shadow=low_x_thresh, highlight=high_x_thresh, midtones=1) + # high_output_image = ps_color_scale_adjustment(low_ouput_image, shadow=0, highlight=high_x_thresh, midtones=1) + + # # 人体贴图和黑色背景交界处不进行锐化 + # gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY) + # _, thresh = cv2.threshold(gray, 2, 255, cv2.THRESH_BINARY) + # kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) + # gradient = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel) + # roi_gradient = cv2.bitwise_and(high_output_image, high_output_image, mask=gradient) + + # # 锐化滤波器 + # # sharpened_image = sharpening_filter(high_output_image) + # sharpened_image = reduce_sharpness(high_output_image, factor=4) + # # 将原图边界替换锐化后的图片边界 + # sharpened_image[gradient != 0] = roi_gradient[gradient != 0] + + # 直方图标记并保存 + # show_histogram(input_image, img_id, low_x_thresh, high_x_thresh) + cv2.imwrite(jpg_path, high_output_image, [cv2.IMWRITE_JPEG_QUALITY, 95]) # 保存图片的质量是原图的 95% + + +#获取该笔订单的信息,判断是否存在,不存在就跳过 +def getInfoByPrintId(print_id): + res = requests.get(f'{get_pid_by_printid_url}?print_id={print_id}') + print('获取pid:', f'{get_pid_by_printid_url}?print_id={print_id}', res.text) + resCode = json.loads(res.text)['code'] + #该笔订单的获取信息有误,可能信息还没有成功 + if int(resCode) != 1000: + tempMesg = json.loads(res.text)['message'] + #判断是否包含 不存在 + if tempMesg == "打印订单不存在": + print(f"打印ID{print_id}打印订单不存在,跳过") + # res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') + #os.system(f'blender -b -P fill_dm_code.py') + return False + + print("获取打印任务信息有问题,重新任务队列,打印id-",print_id,"重新执行脚底板任务,等待20s") + time.sleep(20) + #将pid 重新扔进队列 + r.lpush("model:foot", print_id) + return False + return res.text + +#获取订单的坐标信息,判断是否有,没有的话就重新生成坐标信息 +def getQrPosition(print_id,pid,order_id): + #从云端获取qr_position,如果获取为空,调用cal_foot_position.py计算并上传qr_position,再重新读取qr_position.txt + res = requests.get(f'{get_qr_position_url}?print_id={print_id}') + print('从云端获取的qr_position1:', res.text) + codeTemp = json.loads(res.text)['code'] + if str(codeTemp) == "-1": + print(f"获取打印ID{print_id}的脚底板坐标失败,进行下一笔订单的处理") + return False + qr_position = json.loads(res.text)['data']['position_data'] + if qr_position == '': + print(f"pid={pid},打印id={print_id} order_id={order_id},云端获取的坐标数据为空,进行重新计算") + time.sleep(3) + print('qr_position为空,调用cal_foot_position.py计算并上传qr_position') + os.system(f'blender -b -P cal_foot_position.py -- {pid}_{order_id}_{print_id}') + print("延时20s,等待计算脚底板坐标") + time.sleep(20) + res = requests.get(f'{get_qr_position_url}?print_id={print_id}') + print(f"pid={pid},打印id={print_id} order_id={order_id},重新获取qr_position",res.text) + print('从云端获取的qr_position3:', res.text) + if str(json.loads(res.text)['code']) == "-1": + print(f"pid={pid},打印id={print_id} order_id={order_id},重新获取qr_position 失败",res.text) + return False + qr_position = json.loads(res.text)['data']['position_data'] + if qr_position == '': + #跳过该笔订单 + print(f"pid={pid},打印id={print_id} order_id={order_id},重新获取qr_position 为空,跳过脚底板的处理,执行下一笔任务") + #移除该脚底板的面积处理 + res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') + return False + else: + qr_position = json.loads(qr_position) + + if qr_position == "": + print("获取脚底坐标数据为空,重新任务队列,打印id-",print_id,"重新执行脚底板任务") + #将pid 重新扔进队列 + r.lpush("model:foot", print_id) + return False + + return qr_position + +def main(workdir, r, print_id): + + resText = getInfoByPrintId(print_id) + print(f"根据打印ID{print_id}获取订单信息-内容{resText}") + if resText == False: + print(f"根据打印ID{print_id}获取订单信息失败") + return + arrResText = json.loads(resText) + #订单的相关信息 + use_foot_type = arrResText['data']['use_foot_type'] #构建类型 + pid = arrResText['data']['pid'] #pid + order_id = arrResText['data']['order_id'] # order_id + + #文件路劲 + filename = os.path.join(workdir, f'{pid}_{order_id}', find_obj(pid, order_id)) + print('导入obj文件:', filename) + + #获取该笔订单的坐标 + qr_position = getQrPosition(print_id,pid,order_id) + if qr_position == False: + return + + if type(qr_position) == str: qr_position = json.loads(qr_position) + print(f'type of qr_position:{type(qr_position)}') + print(f'qr_position:{qr_position}') + + qr_position['location'][2] = -0.1 + + temp_foot_data = print_id + # 根据print_id生成qr码 + qr_path = os.path.join(workdir, f'{pid}_{order_id}' ,'qr.png') + # if use_foot_type == "short_url": + # #调用接口获取短网址信息 + # short_url = get_short_url(print_id) + # if short_url == False: + # return + # temp_foot_data = short_url + # print(f'temp_foot_data---{temp_foot_data}') + gen_data_matrix(print_id,pid, qr_path) + #休眠1秒 + time.sleep(2) + #判断是否存在 qr_path,不存在就执行下一个任务 + if not os.path.exists(qr_path): + #移除该脚底板的面积处理 + print(f"pid={pid},打印id={print_id} order_id={order_id},生成的二维码为空,跳过脚底板的处理,执行下一笔任务") + res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') + #os.system(f'blender -b -P fill_dm_code.py') + return + + #在blender中贴上脚底板二维码的信息 + + # 导入obj文件,重置到标准单位 + bpy.ops.wm.read_homefile() + bpy.context.preferences.view.language = 'en_US' + bpy.ops.object.delete(use_global=False, confirm=False) + bpy.context.scene.unit_settings.scale_length = 0.001 + bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' + bpy.context.scene.unit_settings.mass_unit = 'GRAMS' + + bpy.ops.import_scene.obj(filepath=filename) + #bpy.ops.wm.obj_import(filepath=filename) + obj = bpy.context.selected_objects[0] + bpy.context.view_layer.objects.active = obj + obj.select_set(True) + + pid_objname = find_pid_objname(pid) + + scale = 90 / obj.dimensions.y + obj.scale = (scale, scale, scale) + bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'}) + bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') + obj.location[0] = 0 + obj.location[1] = 0 + bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) + + print(f'qr_position:{qr_position}') + print(f'qr_position_type:{type(qr_position)}') + + # 根据qr_position的值,恢复qr的位置和尺寸,重新生成贴图 + bpy.ops.object.load_reference_image(filepath=os.path.join(workdir, f'{pid}_{order_id}', 'qr.png')) + bpy.context.object.rotation_euler = (math.radians(-180), math.radians(0), qr_position['rotation']) + bpy.ops.transform.translate(value=qr_position['location'], orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False, snap=False, snap_elements={'INCREMENT'}, use_snap_project=False, snap_target='CLOSEST', use_snap_self=True, use_snap_edit=True, use_snap_nonedit=True, use_snap_selectable=False, release_confirm=True) + + bpy.context.object.empty_display_size = qr_position['dimensions'][0] + + qr_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}_{order_id}Tex1_qr.png") + jpg_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg") + #判断是否存在 jpg_path,不存在就执行下一个任务 + if not os.path.exists(jpg_path): + #移除该脚底板的面积处理 + res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') + #os.system(f'blender -b -P fill_dm_code.py') + bpy.ops.wm.quit_blender() + return + + jpg_img = Image.open(jpg_path) + shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_noqr.jpg")) + + bpy.context.scene.eyek.res_x = jpg_img.width + bpy.context.scene.eyek.res_y = jpg_img.height + bpy.context.scene.eyek.path_export_image = qr_path + bpy.data.objects[pid_objname].select_set(True) + bpy.data.objects['Empty'].select_set(True) + bpy.context.view_layer.objects.active = bpy.data.objects[pid_objname] + bpy.ops.eyek.exe() + + #判断qr_path是否存在,不存在就执行下一个任务 + if not os.path.exists(qr_path): + #移除该脚底板的面积处理 + res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') + #os.system(f'blender -b -P fill_dm_code.py') + bpy.ops.wm.quit_blender() + return + + qr_img = Image.open(qr_path) + jpg_img.paste(qr_img, (0, 0), qr_img) + jpg_img.save(jpg_path, quality=90) + shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_qr.jpg")) + + # 加入去灰、锐化 + remove_gray_and_sharpening(jpg_path) + + shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_qr.jpg")) + + #上传脚底板文件 -》 修改为移动脚底板文件 + upload_jpg_mtl(pid, order_id, print_id) + + # plt.axis('equal') + # plt.show() + + # 保存blend文件 + # bpy.ops.wm.save_as_mainfile(filepath=f'{workdir}{pid}_{order_id}/{pid}_qr_end.blend') + bpy.ops.wm.quit_blender() + + # 删除临时文件 + print("workdirworkdir",workdir) + shutil.rmtree(os.path.join(workdir, f'{pid}_{order_id}')) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 运行{print_id}任务完成,继续运行下一个任务') + # restart_current_process("blender -b -P fill_dm_code.py") + +def imagePiex(filePath,piex): + # 打开图片 + image = Image.open(filePath) + # 设置新的最大宽度和高度 + max_size = (piex, piex) + # 保持宽高比缩放图片 + image.thumbnail(max_size) + # 保存调整后的图片 + image.save(filePath) + +#根据print_id 获取 短网址 +def get_short_url(print_id): + res = requests.get(f'{get_short_url_by_print_id}?print_id={print_id}') + resCode = json.loads(res.text)['code'] + #该笔订单的获取信息有误,可能信息还没有成功 + if int(resCode) != 1000: + print("获取短网址信息失败,延时1分钟,重新扔入脚底板队列,打印id-",print_id,"重新执行脚底板任务") + time.sleep(60) + #将pid 重新扔进队列 + r.lpush("model:foot", print_id) + #重新调用脚底板程序 + return False + return json.loads(res.text)['data'] + + +@retry(stop_max_attempt_number=10, wait_fixed=2000) +def upload_jpg_mtl(pid, order_id, print_id): + try: + print("移动脚底板文件") + #判断文件夹是否存在,不存在则创建 + if not os.path.exists(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/')): + os.makedirs(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/'),mode=0o777, exist_ok=True) + else: + #移除路径下的文件 + if os.path.exists(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/{pid}Tex1.jpg')): + os.remove(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/{pid}Tex1.jpg')) + print(f"文件 '{pid}' 已成功删除。") + + shutil.move(os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg"),os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/')) + + + #print('生成贴图完成,开始上传...') + # oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1.{print_id}.jpg', os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg")) + # oss_client.put_object_from_file(f'objs/print/{pid}/{pid}.mtl', os.path.join(workdir,f'{pid}_{order_id}', f"{pid}.mtl")) + # # oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1_noqr.jpg', os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_noqr.jpg")) + + print('更新状态为已生成脚底板二维码') + res = requests.post(f'{upload_qr_position_url}?print_id={print_id}') + # #记录日志 + # logging.error(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}-结果:pid:{pid}-print_id:{print_id} {str(res.text)}") + print('更新返回状态:', f'{upload_qr_position_url}?print_id={print_id}', res.text) + + except Exception as e: + + print("迁移文件出 yichang ") + + +def worker(workdir, r, print_id): + """每个子进程的工作函数,用于处理单个 print_id。""" + try: + main(workdir, r, print_id) + except Exception as e: + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f"处理 print_id {print_id} 时出错: {e}") + +def process_print_ids(workdir, r, pool_size): + """监控 Redis 队列并并行处理 print_id,同时控制进程池的大小。""" + # 创建进程池 + pool = multiprocessing.Pool(processes=pool_size) + + while True: + try: + # 如果队列为空,等待10秒后重试 + if r.llen('model:foot') == 0: + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '队列为空,10秒后重试') + time.sleep(10) + continue + + # 从队列中取出一个 print_id + print_id = r.lpop('model:foot') + if print_id is None: + continue + + # 判断该 print_id 是否已经存在,避免重复处理 + isHaveAlready = 0 + for i in r.lrange('model:foot', 0, -1): + if i == print_id: + isHaveAlready = 1 + break + if isHaveAlready == 1: + continue + + # 如果 print_id 是唯一的,则将任务提交给进程池处理 + print(f"正在处理 print_id: {print_id}") + pool.apply_async(worker, args=(workdir, r, print_id)) # 使用 apply_async 异步执行任务 + + except Exception as e: + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f"出现异常错误: {e}") + r = create_redis_connection() + continue + +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__': + atexit.register(common.notify,"打印工厂-本地虚拟木脚底板处理程序已停止一个") + low_y_limit = 25000 + high_y_limit = 13000 + + get_qr_position_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/getFootCodePositionData' + upload_qr_position_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/updateFootCodeStatus' + get_pid_by_printid_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/getPidByPrintId' + get_short_url_by_print_id = 'https://mp.gray.api.suwa3d.com/api/footCode/qrcode' + # get_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/getFootCodePositionData' + # upload_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/updateFootCodeStatus' + # get_pid_by_printid_url = 'http://172.31.1.254:8199/api/printOrder/getPidByPrintId' + + + r = redis.Redis(host='mark.api.suwa3d.com', password='kcV2000', port=6379, db=6) + # r = redis.Redis(host='172.31.1.254', password='', port=6379, db=6) + AccessKeyId = 'LTAI5tSReWm8hz7dSYxxth8f' + AccessKeySecret = '8ywTDF9upPAtvgXtLKALY2iMYHIxdS' + Endpoint = 'oss-cn-shanghai.aliyuncs.com' + Bucket = 'suwa3d-securedata' + oss_client = oss2.Bucket(oss2.Auth(AccessKeyId, AccessKeySecret), Endpoint, Bucket) + + if platform.system() == 'Windows': + workdir = 'E:\\print\\foot\\' + else: + workdir = '/data/datasets/foot/' + + #构建好的obj文件目录 + sourceFilePath = '/data/datasets/print' + #脚底板最终保存的路径 + resFilePath = '/data/datasets/complate/objs' + print("Usage: blender -b -P fill_dm_code.py") + + # # 控制进程池的大小 + # pool_size = 2 # 设置最大并发进程数为4(根据需求调整) + + # # 创建一个进程池 + # pool = multiprocessing.Pool(processes=pool_size) + + # # 如果传递了参数,则处理特定的 print_ids + # if len(sys.argv) == 5: + # print_ids = sys.argv[-1] + # for print_id in print_ids.split(','): + # main(workdir, r, print_id) + # else: + # # 启动进程池,监控队列并并行处理 print_id + # process_print_ids(workdir, r, pool_size) + + if len(sys.argv) == 5: + print_ids = sys.argv[-1] + for print_id in print_ids.split(','): + main(workdir, r, print_id) + else: + while True: + try: + #判断队列是否为空 + if r.llen('model:foot') == 0: + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '队列为空,10秒后重试') + time.sleep(10) + continue + #不为空,取出队列的第一个值 + print_id = r.lpop('model:foot') + if print_id is None: + continue + #判断是否存在相同的值 + isHaveAlready = 0 + for i in r.lrange('model:foot', 0, -1): + if i == print_id: + isHaveAlready = 1 + break + if isHaveAlready == 1: + continue + print_id = print_id.decode('utf-8') + #获取队列的长度 + lengthQueue = r.llen('model:foot') + print(f"正在处理 print_id: {print_id}, 队列长度: {lengthQueue}") + + #如果是唯一值, 则调用main函数 + main(workdir, r, print_id) + except Exception as e: + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '出现异常错误,5秒后重试',e) + r = create_redis_connection() + continue + diff --git a/gray_auto_convert3d.py b/gray_auto_convert3d.py new file mode 100644 index 0000000..a326083 --- /dev/null +++ b/gray_auto_convert3d.py @@ -0,0 +1,391 @@ +from ctypes import util +import os, oss2, time, redis, requests, shutil, sys, subprocess, json, platform,random +from PIL import Image, ImageDraw, ImageFont +from retrying import retry +import atexit,platform +import get_preview_image +# if platform.system() == 'Windows': +# sys.path.append('libs\\') +sys.path.append('/home/acprint/code/libs/') +import common + + +#创建文本文件 +def creatDoingLog(order_id): + #在指定路径创建一条 order_id.txt 文本 + file_path = f"doingLog/{order_id}.txt" + if os.path.exists(file_path): + return + #创建 + with open(file_path, "w", encoding="utf-8") as file: + file.write("1") + +#移除指定的文本文件 +def removeDoingLog(order_id): + file_path = f"doingLog/{order_id}.txt" + if not os.path.exists(file_path): + return + os.remove(file_path) + +#遍历文件夹,返回指定的order_id +def get_order_id_by_txt(): + #遍历文件夹 + for file in os.listdir("doingLog"): + if file != "": + arrFile = file.split(".") + return arrFile[0] + return None + +def find_blender_bin_path(): + if platform.system() == 'Linux': return '/home/acprint/code/blender/blender' + base_path = 'C:\\Program Files\\Blender Foundation\\' + if os.path.exists(base_path): + for dir in os.listdir(base_path): + if dir.startswith('Blender'): + blender_bin_path = base_path + dir + '\\blender.exe' + return f'"{blender_bin_path}"' + else: + print('未找到blender安装目录') + exit(1) + +@retry(stop_max_attempt_number=10, wait_fixed=3000) +def down_obj_fromoss(pid, print_type=1, order_id=None,download_flag="print_build"): + # print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品 + print('开始下载obj文件...' , pid) + + if not order_id is None: + path = os.path.join(workdir, 'print', f'{pid}_{order_id}') + else: + path = os.path.join(workdir, 'print', pid) + if not os.path.exists(path): os.makedirs(path) + + # 根据前缀获取文件列表 + prefix = f'objs/print/{pid}/' + #根据order_id 获取文件在 oss 上的存储路径 + if order_id is not None: + tempURL = "https://mp.gray.api.suwa3d.com/api/order/getOssSuffixByOrderId?orderId="+str(order_id) + res = requests.get(tempURL) + resCode = res.json()['code'] + if int(resCode) != 1000: + return -1 + print(res.text) + if res.json()['data'] != "": + prefix = f'objs/print/{pid}/{res.json()["data"]}/{str(order_id)}/' + + + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + #判断是否有值,没有值的话就从老的路径再获取一次数据 + if len(filelist) == 0: + filelist = oss2.ObjectIteratorV2(oss_client, prefix=f'objs/print/{pid}/') + + + + find = False + findTex1Jpg = False + for file in filelist: + filename = file.key.split('/')[-1] + if filename == '': continue + if filename.endswith(f'{pid}.obj'): + find = True + + if download_flag == "print_build": + if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg" and filename != str(pid)+"_RB_B_Tex1.jpg": + continue + localfile = os.path.join(path, filename) + res = oss_client.get_object_to_file(file.key, localfile) + print(f'下载文件:{file.key},状态:{res.status}') + + if not find: + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == '': continue + if filename.endswith(f'.obj'): + find = True + + # if download_flag == "print_build": + # if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": + # continue + localfile = os.path.join(path, filename) + res = oss_client.get_object_to_file(file.key, localfile) + print(f'下载文件:{file.key},状态:{res.status}') + break + + if find: + os.rename(localfile, os.path.join(path,f"{pid}.obj")) + #重命名文件 + + #判断是否有Tex1.jpg + if not os.path.exists(os.path.join(path,f"{pid}Tex1.jpg")): + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == '': continue + if filename.endswith(f'{pid}.jpg'): + findTex1Jpg = True + + # if download_flag == "print_build": + # if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": + # continue + localfile = os.path.join(path, filename) + res = oss_client.get_object_to_file(file.key, localfile) + print(f'下载文件:{file.key},状态:{res.status}') + break + + if findTex1Jpg: + os.rename(localfile, os.path.join(path,f"{pid}Tex1.jpg")) + #重命名文件 + + # for file in filelist: + # filename = file.key.split('/')[-1] + # if filename == '': continue + # if filename.endswith(f'{pid}.obj'): + # find = True + # if filename.endswith('.obj'): + # print('找到其他obj文件,采用这个文件来生成需要的尺寸', file) + # shutil.copy(os.path.join(path, file), os.path.join(path, f'{pid}.obj')) + # find = True + # break + if not find: + print('找不到obj文件,跳过') + common.notify(f"{pid}-构建打印文件,找不到obj文件,异常,跳过该任务处理") + # sys.exit(1) + os.system(f'python auto_convert3d.py') + return + + # print('下载完成后静默10秒,等待文件写入完成') + # time.sleep(10) +#根据pid获取orderId +def getPidFromOrderId(orderId): + getPidFromOrderId_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/info' + print(f'{getPidFromOrderId_url}?id={orderId}') + res = requests.get(f'{getPidFromOrderId_url}?id={orderId}') + resCode = res.json()['code'] + if int(resCode) != 1000: + return -1 + print(res.text) + return res.json()['data'] + +def detect_obj4print(pid, orderId): + for file in os.listdir(os.path.join(workdir, 'print', f'{pid}_{orderId}')): + if file.endswith('.obj') and 'x' in file: + return True + + +def restart_current_process(new_command): + try: + # 保存新进程的命令 + command = new_command.split() + + # 启动新进程 + new_process = subprocess.Popen(command) + + # 打印新进程的PID + print(f"New process started with PID: {new_process.pid}") + + # 终止当前进程 + os._exit(0) + + except Exception as e: + print(f"An error occurred: {e}") + + +def make3d4print_task(r): + + + tempOrderId = "0" + try: + if r.llen('model:printOrder') == 0: + time.sleep(5) + + # tempOrderId = get_order_id_by_txt() + # if tempOrderId == "0": + # return + except Exception as e: + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), 'redis连接异常,重新连接') + print(e) + time.sleep(5) + r = redis.Redis(host='106.14.158.208', password='kcV2000', port=6379, db=6) + return + orderId = None + if tempOrderId == "0": + orderId = r.lpop('model:printOrder') + else: + orderId = tempOrderId + + if orderId is None: return + + if type(orderId) != str: + orderId = orderId.decode('utf-8') + # orderId = 56077 + res = getPidFromOrderId(orderId) + if res == -1: + print("查询打印订单信息失败,重新开启进程") + #os.system(f'python auto_convert3d.py') + return + + #根据 buy_type 判断 是否是冰箱贴 或者徽章 + buyType = str(res['buy_type']) + if buyType == "2" or buyType == "3": + #塞入到no_resize 队列 + r.lpush("model:noresize", orderId) + return + + pid = str(res['pid']) + if pid == "88985": + return + if pid == "": + return + #创建正在处理的文本内容 + creatDoingLog(orderId) + + print_type = res['print_type'] + digital_type = res['digital_type'] # 0: 只有手办 1: 只有数字模型 2: 手办+数字模型 + + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成待打印模型 start', ) + isFindObj = False + down_obj_fromoss(pid, print_type, orderId,download_flag="print_build") + # 获取程序运行当前目录 + resize_py_path = os.path.join(os.getcwd(), 'blender', 'resize_model.py') + print(f'{blenderbin} -b -P {resize_py_path} -- {orderId}') + os.system(f'{blenderbin} -b -P {resize_py_path} -- {orderId}') + if not detect_obj4print(pid, orderId): + print('obj文件生成异常,退出,重新执行') + restart_current_process("python auto_convert3d.py") + return + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成待打印模型 end') + + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 处理鼻孔 start') + if os.path.exists(f'{workdir}/print/{pid}_{orderId}/{pid}Tex1_old.jpg'): + print('已经处理过鼻孔,跳过') + else: + os.system(f'python fix_nose.py {pid}_{orderId}') + #上传jpg文件 + #oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1.jpg', os.path.join(workdir, 'print', f'{pid}_{orderId}', f'{pid}Tex1.jpg')) + # oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1_old.jpg', os.path.join(workdir, 'print', f'{pid}_{orderId}', f'{pid}Tex1_old.jpg')) + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 处理鼻孔 end') + + # print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成脚底板二维码 start') + # os.system(f'{blenderbin} -b -P d:\\apps\\blender\\auto_dm.py -- {pid}_{orderId}') + # print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成脚底板二维码 end') + + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 上传生成的模型 start') + + path = os.path.join(workdir, 'print', f'{pid}_{orderId}') + + + # 如果指定文件夹目标文件存在,则先删除 + if os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): + delete_files_in_directory(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) + + for file in os.listdir(path): + # 跳过一些不需要上传的文件 + if file in [f'{pid}.png',f'{pid}_old.jpg', f'{pid}.obj', f'{pid}_decimate.glb', f'{pid}_decimate.obj', f'{pid}_decimate.mtl', f'{pid}Tex1_decimate.jpg', f'{pid}_original.obj', f'{pid}_original.mtl']: continue + print("当前目录",os.path.join(path, file)) + + print(f"复制文件路径-{file}") + #将文件移动到指定目录 + if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/')): + os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/'),mode=0o777, exist_ok=True) + + if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): + os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/'),mode=0o777, exist_ok=True) + + shutil.move(os.path.join(path, file), os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) + + + # oss_client.put_object_from_file(f'objs/print/{pid}/{file}', os.path.join(path, file)) + + # texture_file = os.path.join(path, f'{pid}Tex1_decimate.jpg') + # if os.path.exists(texture_file): + # img = Image.open(texture_file) + # img = img.resize((int(img.size[0] * 0.5), int(img.size[1] * 0.5))) + # img.save(texture_file, quality=90, optimize=True) + # print('resize texture file to 50% success') + + input = os.path.join(path, f'{pid}_decimate.obj') + output = os.path.join(path, f'{pid}_decimate.glb') + # os.system(f'gltfpack -c -i {input} -o {output}') + # oss_client.put_object_from_file(f'glbs/3d/{pid}.glb', output) + + shutil.rmtree(path, ignore_errors=True) + + print(f'{update_makeprintobj_status_url}?id={orderId}') + if digital_type == 1: + print('只有数字模型,不需要推送手办打印任务,仍有调用接口') + + res = requests.get(f'{update_makeprintobj_status_url}?id={orderId}') + print('更新打印状态:', res.text) + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 上传生成的模型 end') + removeDoingLog(orderId) + #生成封面图片 + print('小票封面图处理中....') + get_preview_image.createImage(pid) + #os.system(f'python get_preview_image.py {pid}') + + print(f"{pid}-已处理结束") + + restart_current_process("python auto_convert3d.py") + + +def delete_files_in_directory(directory): + for file_name in os.listdir(directory): + file_path = os.path.join(directory, file_name) + try: + if os.path.isfile(file_path): + os.remove(file_path) + print(f"Deleted: {file_path}") + except Exception as e: + print(f"Error deleting {file_path}: {e}") + + +def create_redis_connection(): + """创建 Redis 连接,若连接失败则重试""" + while True: + try: + r = redis.Redis(host="mark.api.suwa3d.com", password="kcV2000", port=6379, db=6) + # 尝试进行一次操作,检查连接是否有效 + r.ping() # ping 操作是一个简单的连接测试 + print("Redis连接成功!") + return r + except ConnectionError: + print("Redis连接失败,正在重试...") + time.sleep(5) + + +def main(r): + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '模型生成程序 start') + while True: + try: + #构建打印文件 + make3d4print_task(r) + except Exception as e: + print(f'出现异常:{e}') + time.sleep(15) + r = create_redis_connection() + continue + +if __name__ == '__main__': + + # atexit.register(common.notify,"虚拟机,生成打印任务程序停止") + AccessKeyId = 'LTAI5tSReWm8hz7dSYxxth8f' + AccessKeySecret = '8ywTDF9upPAtvgXtLKALY2iMYHIxdS' + Endpoint = 'oss-cn-shanghai.aliyuncs.com' + Bucket = 'suwa3d-securedata' + oss_client = oss2.Bucket(oss2.Auth(AccessKeyId, AccessKeySecret), Endpoint, Bucket) + update_check_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingPlatformCheckingStatus' + update_team_check_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingTeamCheckingStatus' + update_status_printstatus_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateBuildPrintModelStatus' + update_makeprintobj_status_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/updateMakePrintObjSucceed' + getRepairInfo_url = 'https://repair.gray.api.suwa3d.com/api/modelRepairOrder/teamCheckGLBInfo' + update_repair_status_url = 'https://repair.gray.api.suwa3d.com/api/modelRepairOrder/updateStatusToWaitingTeamCheckingStatus' + if platform.system() == 'Windows': + workdir = 'E:\\' + else: + workdir = '/data/datasets/' + blenderbin = find_blender_bin_path() + + r = create_redis_connection() + #E:\\complate/objs/147852_54579/ + # os.remove(os.path.join(workdir, f'complate/objs/147852_54579/')) + main(r) \ No newline at end of file diff --git a/gray_no_resize.py b/gray_no_resize.py new file mode 100644 index 0000000..da77e3f --- /dev/null +++ b/gray_no_resize.py @@ -0,0 +1,249 @@ +from ctypes import util +import os, oss2, time, redis, requests, shutil, sys, subprocess, json, platform,random +from PIL import Image, ImageDraw, ImageFont +from retrying import retry +import atexit,platform +import get_preview_image +# if platform.system() == 'Windows': +# sys.path.append('libs\\') +sys.path.append('/home/acprint/code/libs/') +import common + +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) + + +def main(r): + while True: + try: + #构建打印文件 + make3d4print_task(r) + except Exception as e: + print(f'出现异常:{e}') + time.sleep(15) + r = create_redis_connection() + continue + +#根据pid获取orderId +def getPidFromOrderId(orderId): + getPidFromOrderId_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/info' + print(f'{getPidFromOrderId_url}?id={orderId}') + res = requests.get(f'{getPidFromOrderId_url}?id={orderId}') + resCode = res.json()['code'] + if int(resCode) != 1000: + return -1 + print(res.text) + return res.json()['data'] + +def down_obj_fromoss(pid, print_type=1, order_id=None,download_flag="print_build"): + # print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品 + print('开始下载obj文件...' , pid) + + if not order_id is None: + path = os.path.join(workdir, 'print', f'{pid}_{order_id}') + + if not os.path.exists(path): os.makedirs(path) + + # 根据前缀获取文件列表 + prefix = f'objs/print/{pid}/' + #根据order_id 获取文件在 oss 上的存储路径 + if order_id is not None: + tempURL = "https://mp.gray.api.suwa3d.com/api/order/getOssSuffixByOrderId?orderId="+str(order_id) + res = requests.get(tempURL) + resCode = res.json()['code'] + if int(resCode) != 1000: + return -1 + print(res.text) + if res.json()['data'] != "": + prefix = f'objs/print/{pid}/{res.json()["data"]}/{order_id}/' + + + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + find = False + findTex1Jpg = False + for file in filelist: + filename = file.key.split('/')[-1] + if filename == '': continue + if filename.endswith(f'{pid}.obj'): + find = True + + if download_flag == "print_build": + if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": + continue + localfile = os.path.join(path, filename) + res = oss_client.get_object_to_file(file.key, localfile) + print(f'下载文件:{file.key},状态:{res.status}') + + if not find: + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == '': continue + if filename.endswith(f'.obj'): + find = True + + # if download_flag == "print_build": + # if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": + # continue + localfile = os.path.join(path, filename) + res = oss_client.get_object_to_file(file.key, localfile) + print(f'下载文件:{file.key},状态:{res.status}') + break + + if find: + os.rename(localfile, os.path.join(path,f"{pid}.obj")) + #重命名文件 + + #判断是否有Tex1.jpg + if not os.path.exists(os.path.join(path,f"{pid}Tex1.jpg")): + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == '': continue + if filename.endswith(f'{pid}.jpg'): + findTex1Jpg = True + + + localfile = os.path.join(path, filename) + res = oss_client.get_object_to_file(file.key, localfile) + print(f'下载文件:{file.key},状态:{res.status}') + break + + if findTex1Jpg: + os.rename(localfile, os.path.join(path,f"{pid}Tex1.jpg")) + #重命名文件 + +def delete_files_in_directory(directory): + for file_name in os.listdir(directory): + file_path = os.path.join(directory, file_name) + try: + if os.path.isfile(file_path): + os.remove(file_path) + print(f"Deleted: {file_path}") + except Exception as e: + print(f"Error deleting {file_path}: {e}") + + +def make3d4print_task(r): + try: + if r.llen('model:noresize') == 0: + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '暂无可处理的数据') + time.sleep(5) + except Exception as e: + time.sleep(5) + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), 'redis连接异常,重新连接') + r = redis.Redis(host='106.14.158.208', password='kcV2000', port=6379, db=6) + return + orderId = r.lpop('model:noresize') + if orderId is None: return + + # arrOrder = orderIdStr.decode('utf-8').split('_') + # orderId = arrOrder[0] + # size = arrOrder[1] + + if type(orderId) != str: + orderId = orderId.decode('utf-8') + # orderId = 56077 + res = getPidFromOrderId(orderId) + if res == -1: + print("查询打印订单信息失败,重新开启进程") + #os.system(f'python auto_convert3d.py') + return + pid = str(res['pid']) + if pid == "88985": + return + if pid == "": + return + + down_obj_fromoss(pid, 1, orderId,download_flag="print_build") + + + path = os.path.join(workdir, 'print', f'{pid}_{orderId}') + + #获取打印尺寸信息 + get_printsize_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/info' + res = requests.get(f'{get_printsize_url}?id={orderId}') + print('获取打印尺寸:', res.text) + objName = "" + for f in res.json()['data']['fileList']: + if "undefined" in f: + continue + try: + objName = f + break + except Exception as e: + print("eeee",e) + return + + print("obj尺寸名称",objName) + + + # 如果指定文件夹目标文件存在,则先删除 + if os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): + delete_files_in_directory(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) + + for file in os.listdir(path): + # 跳过一些不需要上传的文件 + # if file in [f'{pid}.png',f'{pid}_old.jpg', f'{pid}.obj', f'{pid}_decimate.glb', f'{pid}_decimate.obj', f'{pid}_decimate.mtl', f'{pid}Tex1_decimate.jpg', f'{pid}_original.obj', f'{pid}_original.mtl']: continue + # print("当前目录",os.path.join(path, file)) + + #将文件移动到指定目录 + if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/')): + os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/'),mode=0o777, exist_ok=True) + + if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): + os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/'),mode=0o777, exist_ok=True) + + shutil.move(os.path.join(path, file), os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) + #判断是否是 Obj, 如果是的话要进行重命名 + if file.endswith(f'{pid}.obj'): + os.rename(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/{file}'), os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/{objName}')) + print("重命名成功") + + + + res = requests.get(f'{update_makeprintobj_status_url}?id={orderId}') + print('更新打印状态:', res.text) + print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 上传生成的模型 end') + #生成封面图片 + print('小票封面图处理中....') + get_preview_image.createImage(pid) + #os.system(f'python get_preview_image.py {pid}') + print(f"{pid}-已处理结束") + + + + +if __name__ == '__main__': + + # atexit.register(common.notify,"虚拟机,生成打印任务程序停止") + AccessKeyId = 'LTAI5tSReWm8hz7dSYxxth8f' + AccessKeySecret = '8ywTDF9upPAtvgXtLKALY2iMYHIxdS' + Endpoint = 'oss-cn-shanghai.aliyuncs.com' + Bucket = 'suwa3d-securedata' + oss_client = oss2.Bucket(oss2.Auth(AccessKeyId, AccessKeySecret), Endpoint, Bucket) + update_check_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingPlatformCheckingStatus' + update_team_check_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingTeamCheckingStatus' + update_status_printstatus_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateBuildPrintModelStatus' + update_makeprintobj_status_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/updateMakePrintObjSucceed' + getRepairInfo_url = 'https://repair.api.suwa3d.com/api/modelRepairOrder/teamCheckGLBInfo' + update_repair_status_url = 'https://repair.api.suwa3d.com/api/modelRepairOrder/updateStatusToWaitingTeamCheckingStatus' + if platform.system() == 'Windows': + workdir = 'E:\\' + else: + workdir = '/data/datasets/' + + r = create_redis_connection() + #E:\\complate/objs/147852_54579/ + # os.remove(os.path.join(workdir, f'complate/objs/147852_54579/')) + main(r) \ No newline at end of file