Files
zjsh_code_jicheng/LED_send/led_send.py
2025-12-28 00:14:08 +08:00

304 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# coding: utf-8
import os
import cv2
from PIL import Image, ImageDraw, ImageFont
import ctypes
from ctypes import *
import glob
import sys
# ============================================================
# SDK Load
# ============================================================
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
MAIN_SO_NAME = "libbx_sdkDual.so"
MAIN_SO = os.path.join(CURRENT_DIR, MAIN_SO_NAME)
def preload_shared_objects(so_dir):
print(f"自动加载 so 路径:{so_dir}")
if not os.path.isdir(so_dir):
print(f"错误:目录不存在: {so_dir}")
return None
so_list = glob.glob(os.path.join(so_dir, "*.so*"))
iconv_files = [s for s in so_list if "libiconv" in os.path.basename(s)]
loaded = set()
for f in iconv_files:
try:
ctypes.CDLL(f, mode=ctypes.RTLD_GLOBAL)
print(f"已加载 libiconv: {f}")
loaded.add(f)
except Exception as e:
print(f"加载失败 {f}: {e}")
for f in so_list:
if os.path.basename(f) == MAIN_SO_NAME or f in loaded:
continue
try:
ctypes.CDLL(f, mode=ctypes.RTLD_GLOBAL)
print(f"已加载依赖库: {f}")
except Exception as e:
print(f"跳过无法加载的库 {f}: {e}")
if os.path.exists(MAIN_SO):
try:
lib = ctypes.CDLL(MAIN_SO, mode=ctypes.RTLD_GLOBAL)
print(f"成功加载主库: {MAIN_SO}")
return lib
except Exception as e:
print(f"主库加载失败: {MAIN_SO} -> {e}")
return None
else:
print(f"主库不存在: {MAIN_SO}")
return None
os.environ["LD_LIBRARY_PATH"] = CURRENT_DIR + ":" + os.environ.get("LD_LIBRARY_PATH", "")
os.environ["PATH"] = CURRENT_DIR + ":" + os.environ.get("PATH", "")
lib = preload_shared_objects(CURRENT_DIR)
if lib is None:
print("无法加载主库,程序退出")
sys.exit(1)
# ====================== 生成 LED 表格 ======================
def generate_led_table(data, output_path="led_send.png", font_path="msyh.ttc"):
from PIL import Image, ImageDraw, ImageFont
try:
font_title = ImageFont.truetype(font_path, 24)
font_data = ImageFont.truetype(font_path, 20)
font_data_big = ImageFont.truetype(font_path, 22)
font_small = ImageFont.truetype(font_path, 16)
header_font = ImageFont.truetype(font_path, 26)
except IOError:
print("字体未找到,使用默认字体")
font_title = font_data = font_data_big = font_small = ImageFont.load_default()
header_font = ImageFont.load_default()
total_width, total_height = 630, 430
img = Image.new("RGB", (total_width, total_height), (0, 0, 0))
draw = ImageDraw.Draw(img)
col_count = 4
row_count = 8
row_heights = [int(total_height * 0.095)] * 6 + [int(total_height * 0.15), int(total_height * 0.15)]
y_positions = [0]
for h in row_heights:
y_positions.append(y_positions[-1] + h)
col_width = total_width // col_count
# 表头
header_text = "浇筑工序信息屏测试"
bbox = draw.textbbox((0, 0), header_text, font=header_font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
draw.text(((total_width - tw) // 2, 7), header_text, fill="Yellow", font=header_font)
# safe float
try:
task_quantity = float(data.get("TotMete", 0.0))
fixed_value = float(data.get("BetonVolumeAlready", 0.0))
except Exception:
task_quantity = 0.0
fixed_value = 0.0
task_quantity_str = f"{task_quantity}"
fixed_value_str = f"/{fixed_value}"
table_data = [
["本盘方量", "当前模具", "高斗称值", "低斗称值"],
[str(data.get("PlateVolume", "")), str(data.get("MouldCode", "")),
str(data.get("UpperWeight", "")), str(data.get("LowerWeight", ""))],
["投料时间", "当前管片", "砼出料温度", "振捣频率"],
[str(data.get("ProduceStartTime", "")), str(data.get("ArtifactID", "")),
str(data.get("Temper", "")), str(data.get("VibrationFrequency", ""))],
["累计盘次", "隐蔽验收", "车间环温", "任务方量"],
[str(data.get("PlateIDSerial", "")), str(data.get("CheckResult", "")),
str(data.get("WorkshopTemperature", "")), ""],
["配方比例", "", "", ""],
["拆模强度", "", "", ""]
]
# =======================
# 画表格线(只用 line
# =======================
line_color = (255, 255, 255)
line_width = 1
# 横线
for r in range(row_count + 1):
y = y_positions[r] + 40 if r < row_count else y_positions[-1] + 40
draw.line([(0, y), (total_width, y)], fill=line_color, width=line_width)
# 竖线
for c in range(col_count + 1):
x = c * col_width
# 前6行所有竖线
for r in range(6):
y1 = y_positions[r] + 40
y2 = y_positions[r + 1] + 40
draw.line([(x, y1), (x, y2)], fill=line_color, width=line_width)
# 最后两行
y1 = y_positions[6] + 40
y2 = y_positions[8] + 40
if c == 0 or c == col_count: # 左右边框
draw.line([(x, y1), (x, y2)], fill=line_color, width=line_width)
elif c == 1: # 第二列竖线(分隔跨列内容)
draw.line([(x, y1), (x, y2)], fill=line_color, width=line_width)
# 第三列和第四列竖线不画,保持跨列显示
# =======================
# 绘制文本
# =======================
for r in range(row_count):
y1 = y_positions[r] + 40
h = row_heights[r]
for c in range(col_count):
x1 = c * col_width
content = table_data[r][c]
if not content.strip():
if r == 5 and c == 3:
bbox_task = draw.textbbox((0, 0), task_quantity_str, font=font_data)
tw_task = bbox_task[2] - bbox_task[0]
th_task = bbox_task[3] - bbox_task[1]
draw.text((x1 + (col_width - 1.8 * tw_task) // 2, y1 + (h - th_task) // 2),
task_quantity_str, fill="red", font=font_data)
bbox_fixed = draw.textbbox((0, 0), fixed_value_str, font=font_data)
tw_fixed = bbox_fixed[2] - bbox_fixed[0]
draw.text((x1 + (col_width - tw_fixed) // 2 + 0.78 * tw_task,
y1 + (h - th_task) // 2),
fixed_value_str, fill=(0, 255, 0), font=font_data)
continue
is_header = r in (0, 2, 4, 6, 7)
color = (0, 255, 0) if is_header else "red"
if color == "red" and r < 3:
font = font_data_big
elif color == "red" and r >= 6:
font = font_small
else:
font = font_title if is_header else font_data
bbox = draw.textbbox((0, 0), content, font=font)
tw = bbox[2] - bbox[0]
th = bbox[3] - bbox[1]
draw.text((x1 + (col_width - tw) // 2, y1 + (h - th) // 2), content, fill=color, font=font)
# 多行文本居中
def draw_multiline_text_center(draw_obj, x, y, width, height, text, font_obj, fill="red"):
lines = text.split('\n')
bboxs = [draw_obj.textbbox((0, 0), line, font=font_obj) for line in lines]
total_h = sum(b[3] - b[1] for b in bboxs)
cy = y + (height - total_h) // 2
for line, b in zip(lines, bboxs):
w = b[2] - b[0]
h = b[3] - b[1]
draw_obj.text((x + (width - w) // 2, cy), line, fill=fill, font=font_obj)
cy += h
draw_multiline_text_center(draw, col_width * 1, y_positions[6] + 40, col_width * 3, row_heights[6],
str(data.get("FormulaProportion", "")).replace("\r", ""), font_small)
draw_multiline_text_center(draw, col_width * 1, y_positions[7] + 40, col_width * 3, row_heights[7],
f"{data.get('DayStrengthValue', '')}\n{data.get('NihtStrengthValue', '')}",
font_small)
img.save(output_path)
print(f"已生成参数化表格:{output_path}")
# ====================== 动态区结构体 ======================
class EQpageHeader_G6(Structure):
_fields_ = [
("PageStyle", c_uint8), ("DisplayMode", c_uint8), ("ClearMode", c_uint8),
("Speed", c_uint8), ("StayTime", c_uint16), ("RepeatTime", c_uint8),
("ValidLen", c_uint8), ("CartoonFrameRate", c_uint8), ("BackNotValidFlag", c_uint8),
("arrMode", c_uint8), ("fontSize", c_uint8), ("color", c_uint8),
("fontBold", c_uint8), ("fontItalic", c_uint8), ("tdirection", c_uint8),
("txtSpace", c_uint8), ("Valign", c_uint8), ("Halign", c_uint8)
]
lib.bxDual_dynamicArea_AddAreaPic_6G.argtypes = [
c_char_p, c_uint32, c_uint8, c_uint8, c_uint16, c_uint16,
c_uint16, c_uint16, POINTER(EQpageHeader_G6), c_char_p
]
lib.bxDual_dynamicArea_AddAreaPic_6G.restype = c_int
# ====================== 发送动态区帧(丝滑覆盖) ======================
def send_dynamic_frame(ip="10.6.242.2", port=5005, frame=None, filename="led_send.png"):
if frame is None:
print("frame 为空!")
return
target_w, target_h = 630, 435
resized = cv2.resize(frame, (target_w, target_h))
save_path = os.path.join(CURRENT_DIR, filename)
cv2.imwrite(save_path, resized)
page = EQpageHeader_G6()
page.PageStyle = 0
page.DisplayMode = 2
page.ClearMode = 1
page.Speed = 10
page.StayTime = 1000
page.RepeatTime = 1
page.ValidLen = 64
page.CartoonFrameRate = 0
page.BackNotValidFlag = 0
page.arrMode = 1
page.fontSize = 16
page.color = 1
page.fontBold = 0
page.fontItalic = 0
page.tdirection = 0
page.txtSpace = 0
page.Valign = 2
page.Halign = 1
try:
ret = lib.bxDual_dynamicArea_AddAreaPic_6G(
ip.encode("ascii"), port, 2, 0, 0, 0, target_w, target_h,
byref(page), save_path.encode("gb2312")
)
if ret == 0:
print("Frame 覆盖成功!")
else:
print("Frame 发送失败,返回码:", ret)
except Exception as e:
print("调用 AddAreaPic 失败:", e)
def send_led_data(data: dict):
img_path = os.path.join(CURRENT_DIR, "led_send.png")
generate_led_table(data, output_path=img_path)
frame = cv2.imread(img_path)
send_dynamic_frame(frame=frame, filename="led_send.png")
# ============================================================
# 主程序示例
# ============================================================
if __name__ == "__main__":
data = {
"PlateVolume": "2.00",
"MouldCode": "SHR2B1-3",
"ProduceStartTime": "15:06",
"ArtifactID": "QR2B13099115D",
"Temper": "18.6℃",
"PlateIDSerial": "85",
"CheckResult": "合格",
"TotMete": "353.2",
"LowBucketWeighingValue": "75",
"HighBucketWeighingValue": "115",
"WorkshopTemperature": "12.4℃",
"VibrationFrequency": "10min/220HZ",
"FormulaProportion": "水泥:砂:石:粉煤灰:矿粉:外加剂:水\r\n0.70:1.56:2.78:0.15:0.15:0.006:0.33",
"DayStrengthValue": "白班:2024/11/27 22:00抗压 龄期:15h 强度25.9",
"NihtStrengthValue": "晚班:2024/11/26 07:55抗压 龄期:12h 强度25.2"
}
send_led_data(data)