# -*- coding: utf-8 -*-
"""生成边坡变形监测配置工具使用说明书(PDF格式)"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor, black, white
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak
)
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os
# 注册中文字体
font_dir = os.path.join(os.environ.get("WINDIR", r"C:\Windows"), "Fonts")
pdfmetrics.registerFont(TTFont("SimSun", os.path.join(font_dir, "simsun.ttc"), subfontIndex=0))
pdfmetrics.registerFont(TTFont("SimHei", os.path.join(font_dir, "simhei.ttf")))
# 颜色定义
C_PRIMARY = HexColor("#1A3C6E")
C_HEADER_BG = HexColor("#1A3C6E")
C_HEADER_FG = white
C_ROW_ALT = HexColor("#EDF2F9")
C_RED = HexColor("#CC0000")
C_GRAY = HexColor("#555555")
# 样式
styles = getSampleStyleSheet()
style_body = ParagraphStyle("Body", parent=styles["Normal"], fontName="SimSun", fontSize=11, leading=20, spaceAfter=6)
style_h1 = ParagraphStyle("H1", fontName="SimHei", fontSize=18, leading=28, spaceAfter=12, spaceBefore=20, textColor=C_PRIMARY)
style_h2 = ParagraphStyle("H2", fontName="SimHei", fontSize=14, leading=22, spaceAfter=8, spaceBefore=14, textColor=C_PRIMARY)
style_h3 = ParagraphStyle("H3", fontName="SimHei", fontSize=12, leading=18, spaceAfter=6, spaceBefore=10, textColor=C_PRIMARY)
style_bullet = ParagraphStyle("Bullet", parent=style_body, leftIndent=20, bulletIndent=8)
style_cover_title = ParagraphStyle("CoverTitle", fontName="SimHei", fontSize=26, leading=40, alignment=TA_CENTER, textColor=C_PRIMARY)
style_cover_sub = ParagraphStyle("CoverSub", fontName="SimSun", fontSize=14, alignment=TA_CENTER, textColor=C_GRAY)
style_toc = ParagraphStyle("TOC", fontName="SimSun", fontSize=11, leading=18)
style_toc_bold = ParagraphStyle("TOCBold", fontName="SimHei", fontSize=11, leading=18)
style_step = ParagraphStyle("Step", parent=style_body, leftIndent=20)
style_warning = ParagraphStyle("Warning", parent=style_body, textColor=C_RED, fontName="SimHei")
style_q = ParagraphStyle("Q", fontName="SimHei", fontSize=11, leading=20, spaceBefore=10, spaceAfter=2)
style_a = ParagraphStyle("A", fontName="SimSun", fontSize=11, leading=20, leftIndent=20, spaceAfter=6)
def make_table(headers, rows, col_widths=None):
data = [headers] + rows
if col_widths is None:
col_widths = [doc.width / len(headers)] * len(headers)
t = Table(data, colWidths=col_widths, repeatRows=1)
style_cmds = [
("BACKGROUND", (0, 0), (-1, 0), C_HEADER_BG),
("TEXTCOLOR", (0, 0), (-1, 0), C_HEADER_FG),
("FONTNAME", (0, 0), (-1, 0), "SimHei"),
("FONTSIZE", (0, 0), (-1, 0), 11),
("FONTNAME", (0, 1), (-1, -1), "SimSun"),
("FONTSIZE", (0, 1), (-1, -1), 10),
("ALIGN", (0, 0), (-1, -1), "LEFT"),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("GRID", (0, 0), (-1, -1), 0.5, HexColor("#CCCCCC")),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 6),
]
for i in range(1, len(data)):
if i % 2 == 0:
style_cmds.append(("BACKGROUND", (0, i), (-1, i), C_ROW_ALT))
t.setStyle(TableStyle(style_cmds))
return t
def bullet(text):
return Paragraph("o " + text, style_bullet)
def step(num, text):
return Paragraph(str(num) + ". " + text, style_step)
# 构建文档
output_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"边坡变形监测配置工具使用说明书.pdf"
)
doc = SimpleDocTemplate(output_path, pagesize=A4,
leftMargin=2.5*cm, rightMargin=2.5*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm)
story = []
# 封面
story.append(Spacer(1, 6*cm))
story.append(Paragraph("边坡变形监测-变形(位移)
自动测量系统配置工具
使用说明书", style_cover_title))
story.append(Spacer(1, 1.5*cm))
story.append(Paragraph("版本 V2.0", style_cover_sub))
story.append(PageBreak())
# 目录
story.append(Paragraph("目 录", style_h1))
toc_items = [
(True, "1 软件概述"),
(True, "2 运行环境"),
(True, "3 软件安装与启动"),
(False, " 3.1 安装"),
(False, " 3.2 ��动"),
(False, " 3.3 USB转串口驱动安装"),
(True, "4 软件界面说明"),
(False, " 4.1 串口连接区域"),
(False, " 4.2 参数配置区域"),
(False, " 4.3 设备参数信息区域"),
(False, " 4.4 低功耗配置区域"),
(False, " 4.5 功率与激光配置区域"),
(False, " 4.6 通讯日志区域"),
(True, "5 操作指南"),
(False, " 5.1 连接设备"),
(False, " 5.2 查询设备参数"),
(False, " 5.3 修改Lora信道频率"),
(False, " 5.4 全功率时段配置"),
(False, " 5.5 激光控制"),
(False, " 5.6 设备升级"),
(False, " 5.7 发送自定义指令"),
(True, "6 指令参考"),
(True, "7 常见问题与故障排除"),
]
for bold, text in toc_items:
story.append(Paragraph(text, style_toc_bold if bold else style_toc))
story.append(PageBreak())
# 1 软件概述
story.append(Paragraph("1 软件概述", style_h1))
story.append(Paragraph(
"边坡变形监测-变形(位移)自动测量系统配置工具是一款用于激光示踪传感器设备的网关参数配置软件。"
"软件通过RS485串口与传感器设备进行通信,支持查询设备参数、修改Lora无线通信频率、发送自定义指令等操作。",
style_body
))
story.append(Paragraph("主要功能:", style_body))
features = [
"自动识别并列出本机可用串口",
"通过RS485串口连接激光示踪传感器设备",
"一键查询设备全部参数(软件版本、硬件版本、MAC地址、电池电量、注册状态等)",
"修改Lora无线通信信道频率(410.1MHz ~ 492.9MHz,步进200kHz)",
"查看设备低功耗配置参数(采集间隔、上报时间、紧急上报间隔/时长)",
"查看功率与激光参数,并配置全功率工作时间段",
"控制激光开关及设置自动关闭时间",
"通过通用串口升级协议(V1.1)实现设备固件升级",
"支持发送自定义AT指令",
"通讯日志实时显示,方便调试",
]
for f in features:
story.append(bullet(f))
# 2 运行环境
story.append(Paragraph("2 运行环境", style_h1))
story.append(make_table(
["项目", "要求"],
[
["操作系统", "Windows 7 / 8 / 10 / 11"],
["硬件接口", "USB转RS485串口线(或自带RS485接口)"],
["驱动要求", "USB转串口芯片驱动(如CH340/CP2102/FT232等)"],
["运行方式", "独立可执行文件(.exe),无需安装Python环境"],
["屏幕分辨率", "建议1280x720及以上"],
],
col_widths=[4*cm, 12*cm]
))
# 3 软件安装与启动
story.append(Paragraph("3 软件安装与启动", style_h1))
story.append(Paragraph("3.1 安装", style_h2))
story.append(Paragraph(
'本软件为绿色免安装版本,无需安装过程。将"边坡变形监测-变形(位移)自动测量系统配置工具.exe"文件复制到电脑任意目录即可使用。',
style_body
))
story.append(Paragraph("3.2 启动", style_h2))
story.append(Paragraph(
'双击"边坡变形监测-变形(位移)自动测量系统配置工具.exe"文件即可启动软件。',
style_body
))
story.append(Paragraph(
'注意:如果Windows弹出"Windows已保护你的电脑"提示,请点击"更多信息"->"仍要运行"。',
style_body
))
story.append(Paragraph("3.3 USB转串口驱动安装", style_h2))
story.append(Paragraph("使用前需确保USB转RS485串口线对应的驱动已正确安装。常见芯片及驱动如下:", style_body))
story.append(make_table(
["芯片型号", "驱动说明"],
[
["CH340 / CH341", "沁恒官方驱动,安装后在设备管理器中显示为COM口"],
["CP2102 / CP210x", "Silicon Labs官方驱动"],
["FT232", "FTDI官方驱动"],
],
col_widths=[5*cm, 11*cm]
))
story.append(Spacer(1, 6))
story.append(Paragraph(
'驱动安装成功后,将USB转RS485线插入电脑,在"设备管理器->端口(COM和LPT)"中应能看到对应的COM口编号。',
style_body
))
# 4 软件界面说明
story.append(Paragraph("4 软件界面说明", style_h1))
story.append(Paragraph("软件界面从上至下分为5个功能区域:", style_body))
story.append(Paragraph("4.1 串口连接区域", style_h2))
story.append(Paragraph("位于界面顶部,用于选择和配置串口连接参数。", style_body))
story.append(make_table(
["项目", "说明", "默认值"],
[
["串口选择", '下拉框选择可用串口,点击"刷新"按钮可重新扫描', "自动选择第一个"],
["波特率", "串口通信速率", "9600 bps"],
["数据位", "每帧数据位数", "8"],
["停止位", "停止位位数", "1"],
["校验", "奇偶校验方式", "None(无校验)"],
["打开/关闭串口", "按钮切换串口连接状态", "—"],
],
col_widths=[3.5*cm, 8.5*cm, 4*cm]
))
story.append(Paragraph("4.2 参数配置区域", style_h2))
story.append(Paragraph("包含Lora参数、全功率时段配置、激光控制以及查询/升级操作:", style_body))
story.append(Paragraph("Lora 参数:", style_body))
story.append(Paragraph(
"信道频率(fc):下拉选择Lora通信频率,范围410.1MHz ~ 492.9MHz,步进200kHz(共415个信道)。"
'选择后点击"发送"按钮即可修改设备频率。',
style_bullet
))
story.append(Paragraph("全功率时段配置:", style_body))
story.append(Paragraph(
"设置全功率工作时间段:开始时间(0~23时)、持续时间(0~24时,0=关闭),"
'输入后点击"发送"按钮下发配置。',
style_bullet
))
story.append(Paragraph("激光控制:", style_body))
story.append(Paragraph(
'点击"打开"/"关闭"按钮控制激光开关;"自动关闭(min)"设置激光自动关闭时间(0=禁用),点击"设置"下发。',
style_bullet
))
story.append(Paragraph("查询与升级按钮:", style_body))
story.append(Paragraph(
'点击"查询当前配置(para)"按钮,软件向设备发送para指令查询全部参数。'
'查询结果将自动解析并显示在下方参数信息区域。点击"设备升级"按钮可打开设备升级对话框。',
style_bullet
))
story.append(Paragraph("4.3 设备参数信息区域", style_h2))
story.append(Paragraph("显示通过para指令查询到的设备参数,包括:", style_body))
story.append(make_table(
["参数名称", "说明"],
[
["软件版本", "设备固件软件版本号"],
["硬件版本", "设备硬件版本号"],
["本机MAC", "传感器设备的MAC地址"],
["电池电量", "设备当前电池电量"],
["设备类型", "设备类型标识(LocalType)"],
["通信方式", "设备当前通信方式(CommInf)"],
["设备标识", "设备唯一标识(DeviceSign)"],
["网关MAC", "所连接网关的MAC地址"],
["注册状态", "设备在网关上的注册状态"],
["Lora频率", "当前Lora通信频率"],
],
col_widths=[4*cm, 12*cm]
))
story.append(Paragraph("4.4 低功耗配置区域", style_h2))
story.append(Paragraph("显示设备的低功耗运行参数:", style_body))
story.append(make_table(
["参数名称", "说明"],
[
["CI(采集间隔)", "传感器数据采集的时间间隔,单位:秒"],
["RI(上报时间)", "数据上报到网关的时间间隔,单位:分钟/时"],
["EI(紧急上报间隔)", "紧急状态下数据上报的时间间隔,单位:分钟/时"],
["ET(紧急上报时长)", "紧急上报模式持续时间,单位:分钟/时"],
],
col_widths=[5*cm, 11*cm]
))
story.append(Paragraph("4.5 功率与激光配置区域", style_h2))
story.append(Paragraph("显示通过para指令查询到的功率与激光运行参数:", style_body))
story.append(make_table(
["参数名称", "说明"],
[
["开始时间", "全功率工作时间段的开始时刻"],
["结束时间", "全功率工作时间段的结束时刻"],
["全功率时长", "全功率工作持续时间"],
["当前模式", "当前工作模式(全功率/低功耗)"],
["激光状态", "激光当前开关状态(ON/OFF)"],
["自动关闭", "激光自动关闭时间"],
],
col_widths=[5*cm, 11*cm]
))
story.append(Paragraph("4.6 通讯日志区域", style_h2))
story.append(Paragraph("界面底部,分为左右两部分:", style_body))
story.append(Paragraph("日志窗口:实时显示串口通讯内容。不同类型信息用不同颜色区分:", style_body))
story.append(make_table(
["颜色", "含义"],
[
["蓝色", "发送的指令"],
["绿色", "接收到的数据"],
["浅绿色", "系统提示信息"],
["红色", "错误信息"],
],
col_widths=[4*cm, 12*cm]
))
story.append(Spacer(1, 6))
story.append(Paragraph('自定义指令:右侧面板可输入自定义AT指令,点击"发送"或按Enter键发送。', style_body))
# 5 操作指南
story.append(Paragraph("5 操作指南", style_h1))
story.append(Paragraph("5.1 连接设备", style_h2))
steps = [
"将USB转RS485串口线连接到电脑USB接口。",
"将RS485线的A、B端分别连接到传感器设备的RS485接口(注意A接A、B接B)。",
'启动软件,在"串口连接"区域点击"刷新"按钮扫描可用串口。',
"在下拉框中选择对应的COM口。",
"确认波特率等参数与设备一致(默认9600, 8N1)。",
'点击"打开串口"按钮,按钮文字变为"关闭串口"即表示连接成功。通讯日志中将显示连接信息。',
]
for i, s in enumerate(steps, 1):
story.append(step(i, s))
story.append(Paragraph("5.2 查询设备参数", style_h2))
steps = [
'确认串口已成功连接(按钮显示"关闭串口")。',
'点击"查询当前配置(para)"按钮。',
"软件将发送para指令,设备返回参数信息后自动解析。",
"设备参数信息和低功耗配置区域将显示查询到的数值。",
"Lora频率下拉框也会同步更新为当前设备频率。",
]
for i, s in enumerate(steps, 1):
story.append(step(i, s))
story.append(Paragraph("5.3 修改Lora信道频率", style_h2))
steps = [
"确认串口已成功连接。",
'在"Lora 参数"区域的下拉框中选择目标信道频率。',
'点击"发送"按钮,软件将发送"lora fc <频率值>"指令。',
'发送后建议点击"查询当前配置"验证修改是否成功。',
]
for i, s in enumerate(steps, 1):
story.append(step(i, s))
story.append(Paragraph(
"注意:修改Lora频率后,传感器与网关必须使用相同的信道频率才能正常通信。请确保网关的Lora频率也做了相应修改。",
style_warning
))
story.append(Paragraph("5.4 全功率时段配置", style_h2))
steps = [
"确认串口已成功连接。",
'在"参数配置"区域的"全功率时段配置"中输入开始时间(0~23)和持续时间(0~24,0=关闭)。',
'点击"发送"按钮下发配置。',
'发送后建议点击"查询当前配置"验证配置是否生效。',
]
for i, s in enumerate(steps, 1):
story.append(step(i, s))
story.append(Paragraph("5.5 激光控制", style_h2))
steps = [
"确认串口已成功连接。",
'点击"打开"或"关闭"按钮控制激光开关。',
"如需要设置自动关闭时间,在\"自动关闭(min)\"框中输入分钟数(0=禁用),点击\"设置\"下发。",
'发送后建议点击"查询当前配置"验证激光状态。',
]
for i, s in enumerate(steps, 1):
story.append(step(i, s))
story.append(Paragraph("5.6 设备升级", style_h2))
story.append(Paragraph("设备升级用于通过通用串口升级协议对设备固件进行升级。升级期间请保持设备供电正常。", style_body))
steps = [
"确认串口已成功连接。",
'点击"参数配置"区域中的"设备升级"按钮,打开设备升级对话框。',
"在对话框中选择要更新的固件文件(.bin),并确认设备MAC地址(默认自动填充本机MAC)。",
'点击"开始升级"按钮,软件通过协议下发固件并向设备逐包传输。',
"握住安装、进度条显示传输进度;升级日志显示握手/下发/完成等过程信息。",
"升级完成后可在同一对话框再次点击\"开始升级\"重复升级,无需关闭重开。",
]
for i, s in enumerate(steps, 1):
story.append(step(i, s))
story.append(Paragraph(
"注意:升级过程中请勿断开串口或断电,否则可能导致设备无法正常启动。",
style_warning
))
story.append(Paragraph("5.7 发送自定义指令", style_h2))
steps = [
"确认串口已成功连接。",
'在通讯日志区域右侧的"自定义指令"输入框中输入AT指令。',
'点击"发送"按钮或按Enter键发送。',
"通讯日志中将显示发送的指令和设备的回应。",
]
for i, s in enumerate(steps, 1):
story.append(step(i, s))
# 6 指令参考
story.append(Paragraph("6 指令参考", style_h1))
story.append(Paragraph("软件内置以下AT指令:", style_body))
story.append(make_table(
["指令", "功能", "示例"],
[
["para", "查询设备全部参数", "para"],
["lora fc <频率值>", "设置Lora信道频率(单位Hz)", "lora fc 433100000"],
["fpocfg <开始> <持续>", "设置全功率工作时间段(时,0=关闭)", "fpocfg 8 2"],
["laser on / off", "打开/关闭激光", "laser on"],
["laser time ", "设置激光自动关闭时间(0=禁用)", "laser time 10"],
["(自定义指令)", "在自定义指令输入框中输入任意指令", "—"],
],
col_widths=[5*cm, 6*cm, 5*cm]
))
story.append(Spacer(1, 6))
story.append(Paragraph("说明:指令以回车换行(\\r\\n)结尾自动发送,无需手动添加。", style_body))
# 7 常见问题
story.append(Paragraph("7 常见问题与故障排除", style_h1))
qa_items = [
("串口下拉框为空,无法选择串口",
"1. 检查USB转RS485线是否已插入电脑;\n"
"2. 检查设备管理器中是否出现对应的COM口;\n"
"3. 确认USB转串口驱动已正确安装;\n"
'4. 点击"刷新"按钮重新扫描。'),
('点击"打开串口"后提示连接失败',
"1. 确认该COM口未被其他软件占用(如串口助手、Keil等);\n"
"2. 尝试拔插USB线后重试;\n"
"3. 尝试更换USB接口。"),
("发送para指令后无回应",
"1. 确认RS485接线正确(A接A、B接B);\n"
"2. 确认传感器设备已上电;\n"
"3. 确认波特率设置与设备一致(默认9600);\n"
"4. 检查RS485线缆是否损坏。"),
("修改Lora频率后设备无法与网关通信",
"1. 确认传感器和网关使用相同的Lora信道频率;\n"
"2. 修改频率后需等待设备重新连接;\n"
"3. 可通过para命令验证当前频率设置。"),
('参数信息区域显示 "-"',
"1. 表示尚未查询或未收到设备回应;\n"
'2. 请先点击"查询当前配置"按钮;\n'
"3. 检查串口连接是否正常。"),
('Windows提示"已保护你的电脑"无法运行',
"1. 点击更多信息;\n"
"2. 点击仍要运行按钮;\n"
"3. 此提示是因为exe未做代码签名,软件本身安全。"),
("点击设备升级后无握手/升级失败",
"1. 确认设备MAC地址正确;\n"
"2. 确认固件文件为正确的.bin文件;\n"
"3. 确认串口连接正常、设备供电稳定;\n"
"4. 查看通讯日志中的设备调试信息辅助排查。"),
]
for q, a in qa_items:
story.append(Paragraph("Q: " + q, style_q))
story.append(Paragraph(a.replace("\n", "
"), style_a))
# 生成PDF
doc.build(story)
print("说明书已生成: " + output_path)