Files
zjsh_code_jicheng/LED_send/led_send.py
琉璃月光 caeb0457f4 Initial commit
2025-11-18 17:16:08 +08:00

293 lines
12 KiB
Python
Raw 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="simsun.ttc"):
"""
根据接口返回的 Data 生成 LED 显示表格,适配 640x448 LED 屏
"""
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, 30)
except IOError:
print("字体未找到,使用默认字体")
font_title = font_data = font_data_big = font_small = ImageFont.load_default()
header_font = ImageFont.load_default()
total_width, total_height = 640, 448
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[:-1]:
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 parse
try:
task_quantity = float(data.get("TotMete", 0))
except Exception:
task_quantity = 0.0
fixed_value = 214.1
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("HighBucketWeighingValue", "")), str(data.get("LowBucketWeighingValue", ""))],
["投料时间", "当前管片", "砼出料温度", "振捣频率"],
[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", "")), ""],
["配方比例", "", "", ""],
["拆模强度", "", "", ""]
]
# 画表格框
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
if r >= 6 and c == 1:
draw.rectangle([x1, y1, total_width - 1, y1 + h - 1], outline="white", width=1)
break
elif r >= 6 and c > 1:
continue
else:
draw.rectangle([x1, y1, x1 + col_width - 1, y1 + h - 1], outline="white", width=1)
# 绘制文本
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="green", font=font_data)
continue
is_header = r in (0, 2, 4, 6, 7)
color = "green" 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)
y_start = y + (height - total_h) // 2
curr_y = y_start
for line, b in zip(lines, bboxs):
w = b[2] - b[0]
h = b[3] - b[1]
draw_obj.text((x + (width - w) // 2, curr_y), line, fill=fill, font=font_obj)
curr_y += 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_DelArea_6G.argtypes = [c_char_p, c_uint32, 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_DelArea_6G.restype = c_int
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 为空!") #因为相机SDK接口需要的是待发送图片的地址所以加上确认需要发送图片是否存在。
return
target_w, target_h = 640, 448
resized = cv2.resize(frame, (target_w, target_h))
current_dir = os.path.dirname(os.path.abspath(__file__))
save_path = os.path.join(current_dir, filename)
# 使用 cv2.imwrite 保存确保文件编码一致
cv2.imwrite(save_path, resized)
# 这些参数都可以设置,我备注一下参数名称和调节的信息
page = EQpageHeader_G6()
page.PageStyle = 0 #数据页类型默认为0
page.DisplayMode = 2 #显示方式: 0x00 :随机显示 0x01 :静止显示 0x02 :快速打出 0x03 :向左移动 0x04 :向左连移 0x05 :向上移动 0x06 :向上连移 0x07 :闪烁 ......
page.ClearMode = 1 #退出方式/清屏方式
page.Speed = 10 #速度等级/背景速度等级
page.StayTime = 1000 #停留时间, 单位为 10ms
page.RepeatTime = 1 #重复次数/背景拼接步长(左右拼接下为宽度, 上下拼接为高度)
page.ValidLen = 64 #用法比较复杂请参考协议,默认不动
page.CartoonFrameRate = 0 #特技为动画方式时,该值代表其帧率
page.BackNotValidFlag = 0 #背景无效标志
#字体信息
page.arrMode = 1 #排列方式--单行多行
page.fontSize = 16 #字体大小
page.color = 1 #字体颜色 E_Color_G56此通过此枚举值可以直接配置七彩色如果大于枚举范围使用RGB888模式
page.fontBold = 0 #是否为粗体
page.fontItalic = 0 #是否为斜体
page.tdirection = 0 #文字方向
page.txtSpace = 0 #文字间隔
page.Valign = 2 #纵向对齐方式0系统自适应、1上对齐、2居中、3下对齐
page.Halign = 1 #横向对齐方式0系统自适应、1左对齐、2居中、3右对齐
print("删除旧动态区 ...")
try:
ret_del = lib.bxDual_dynamicArea_DelArea_6G(ip.encode(), port, 0xFF)
print("删除返回码:", ret_del)
except Exception as e:
print("调用 DelArea 失败:", e)
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)
#这里读取图片是为了保证生成图片函数已经在改文件夹下生成了图片因为相机SDK接口需要的是待发送图片的地址所以加上确认。
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)