12 Commits

Author SHA1 Message Date
YuanHongbin 52435a21dd docs(manual): 说明书补充设备升级章节/FAQ/注意事项/封面版本, 文档版本V1.2 2026-09-24 11:16:02 +08:00
YuanHongbin e2f1fa1206 feat(device): 设备管理删除后即时移除列表项,不再触发重新查询;版本更新至V1.2.2并同步说明书 2026-09-24 10:57:02 +08:00
YuanHongbin 62f4032eca v1.2.1: 修复设备管理查询时数据流中途误触发解析导致表格被清零,版本号更新至V1.2.1 2026-09-17 18:42:16 +08:00
YuanHongbin 455d083c50 修复设备管理查询时数据流中途触发解析导致表格被清零的问题 2026-09-17 18:41:23 +08:00
YuanHongbin 489c34e16a 宽带Lora分支:起始频率改为433.1MHz(433.1MHz~492.6MHz,步进0.5MHz,120信道,信道0起始) 2026-09-07 18:21:27 +08:00
YuanHongbin 8382fe6f36 使用说明书:适用设备去掉括号内型号描述 2026-09-07 17:10:40 +08:00
YuanHongbin 03474850d6 更新使用说明书PDF(宽带版:410.1MHz~492.6MHz 166信道信道0起始) 2026-09-07 17:08:14 +08:00
YuanHongbin ea029ca6b7 标题栏增加版本标识:网关配置工具(宽带版 V1.2.0) 2026-09-07 16:35:53 +08:00
YuanHongbin dc598d8912 宽带Lora分支:信道频率范围410.1MHz~492.6MHz(步进0.5MHz,166信道,信道0起始) 2026-09-07 16:06:12 +08:00
YuanHongbin f14aa0bcf0 更新使用说明书PDF(Lora信道0起始、波特率可选等) 2026-09-07 11:23:14 +08:00
YuanHongbin c346b57aef Lora信道从0编号、波特率可选、修复通讯日志打印不全
- Lora信道以信道0为起点(470.1MHz~492.6MHz,共46信道)
- 波特率改为可编辑下拉框,默认500000
- 修复para等大数据量响应日志只打印一小段:延迟解析避免UI阻塞导致串口缓冲溢出丢包
- 说明书同步更新
2026-09-02 10:50:21 +08:00
YuanHongbin 1bccbb873f 新增设备管理与升级功能,优化参数信息显示
- 修复点击设备升级闪退问题
- 升级弹窗MAC提示改为81-00-00-06-00-00,进度条颜色不再随失焦变化
- 新增设备查询(culs)/设备删除(cudel)功能
- 设备列表显示Laser/FPO/LpCfg参数
- 设备参数信息区重新排布,显示通讯单元读取间隔
- 说明书同步更新
2026-09-01 11:43:47 +08:00
3 changed files with 794 additions and 434 deletions
+734 -307
View File
File diff suppressed because it is too large Load Diff
+58 -125
View File
@@ -147,10 +147,10 @@ story.append(Spacer(1, 10 * mm))
cover_info = [ cover_info = [
["软件名称", "网关配置工具"], ["软件名称", "网关配置工具"],
["适用设备", "物联网网关(HC32F460 + 4G/Cat1 + Ethernet)"], ["适用设备", "物联网网关"],
["通信接口", "串口(UART),波特率 500000"], ["通信接口", "串口(UART),波特率默认500000可修改"],
["运行环境", "Windows 7/10/11"], ["运行环境", "Windows 7/10/11"],
["文档版本", "V1.0"], ["文档版本", "V1.1"],
] ]
cover_table = Table(cover_info, colWidths=[40 * mm, 90 * mm]) cover_table = Table(cover_info, colWidths=[40 * mm, 90 * mm])
cover_table.setStyle(TableStyle([ cover_table.setStyle(TableStyle([
@@ -184,12 +184,10 @@ toc_items = [
("3", "功能详解"), ("3", "功能详解"),
(" 3.1", "串口连接"), (" 3.1", "串口连接"),
(" 3.2", "查看配置(para)"), (" 3.2", "查看配置(para)"),
(" 3.3", "通讯方式切换"), (" 3.3", "Lora信道配置"),
(" 3.4", "ETH网络参数配置"), (" 3.4", "自定义指令"),
(" 3.5", "Lora信道配置"), (" 3.5", "通讯日志"),
(" 3.6", "低功耗配置"), (" 3.6", "设备管理"),
(" 3.7", "自定义指令"),
(" 3.8", "通讯日志"),
("4", "指令参考"), ("4", "指令参考"),
("5", "常见问题(FAQ)"), ("5", "常见问题(FAQ)"),
("6", "注意事项"), ("6", "注意事项"),
@@ -211,14 +209,12 @@ story.append(Spacer(1, 2 * mm))
features = [ features = [
["功能模块", "说明"], ["功能模块", "说明"],
["串口通信", "通过USB转串口连接网关设备,波特率500000,实时收发指令"], ["串口通信", "通过USB转串口连接网关设备,波特率默认500000可自行修改,实时收发指令"],
["配置读取", "一键读取网关全部参数,包括软件/硬件版本、MAC地址、电池状态、注册状态等"], ["配置读取", "一键读取网关全部参数,包括软件/硬件版本、MAC地址、电池状态、注册状态等"],
["通讯方式切换", "支持Cat1(4G)和ETH(以太网)两种通讯方式的切换"], ["Lora信道配置", "选择并设置Lora通信信道频率(433.1MHz ~ 492.6MHz)"],
["ETH参数配置", "配置以太网相关参数:IP地址、端口、子网掩码、网关、服务器地址等"],
["Lora信道配置", "选择并设置Lora通信信道频率(410.1MHz ~ 524.9MHz)"],
["低功耗配置", "查看采集间隔、上报间隔、紧急上报等低功耗参数"],
["自定义指令", "支持手动输入任意指令与网关交互"], ["自定义指令", "支持手动输入任意指令与网关交互"],
["通讯日志", "实时显示发送和接收的通讯数据,便于调试"], ["通讯日志", "实时显示发送和接收的通讯数据,便于调试"],
["设备管理", "查询/删除网关已注册的通讯单元设备,删除后即时更新列表"],
] ]
story.append(make_table(features[0], features[1:], col_widths=[35 * mm, 115 * mm])) story.append(make_table(features[0], features[1:], col_widths=[35 * mm, 115 * mm]))
story.append(Spacer(1, 3 * mm)) story.append(Spacer(1, 3 * mm))
@@ -235,15 +231,11 @@ story.append(make_table(["项目", "要求"], req_rows, col_widths=[35 * mm, 115
story.append(heading2("1.3 软件界面总览")) story.append(heading2("1.3 软件界面总览"))
story.append(body("软件界面自上而下分为以下几个区域:")) story.append(body("软件界面自上而下分为以下几个区域:"))
ui_rows = [ ui_rows = [
[f"{LQ}串口连接{RQ}区", "选择串口、刷新端口列表、打开/关闭串口连接"], [f"{LQ}串口连接{RQ}区", "选择串口、刷新端口列表、打开/关闭串口"],
[f"基本指令区", f"查看配置、切换通讯方式(Cat1/ETH)的快捷按钮"], [f"{LQ}参数配置{RQ}区", f"设置LoRa信道频率、{LQ}查看配置(para){RQ}、{LQ}设备升级{RQ}按钮"],
[f"当前网关配置", f"显示网关读取到的各项参数(版本、MAC、电池、注册状态等)"], [f"{LQ}设备参数信息{RQ}区", f"显示网关读取到的各项参数(版本、MAC、电池、注册状态等)"],
[f"ETH网络参数", f"显示当前ETH网络参数(IP、端口、掩码、服务器地址等)"], [f"{LQ}通讯日志{RQ}区", f"实时显示收发数据(蓝色=发送,橙色=接收)"],
[f"低功耗配置", f"显示采集间隔、上报间隔等低功耗相关参数"], [f"{LQ}自定义指令{RQ}区", f"手动输入任意指令发送"],
[f"ETH参数配置", f"输入并发送ETH相关配置参数"],
[f"Lora信道配置", f"选择Lora信道并发送"],
[f"通讯日志", f"实时显示收发数据(蓝色=发送,橙色=接收)"],
[f"自定义指令", f"手动输入任意指令发送"],
] ]
story.append(make_table(["区域", "功能说明"], ui_rows, col_widths=[35 * mm, 115 * mm])) story.append(make_table(["区域", "功能说明"], ui_rows, col_widths=[35 * mm, 115 * mm]))
@@ -282,9 +274,9 @@ story.append(hr())
# 3.1 # 3.1
story.append(heading2("3.1 串口连接")) story.append(heading2("3.1 串口连接"))
story.append(body("串口连接是使用本工具的前提。软件固定使用以下串口参数:")) story.append(body("串口连接是使用本工具的前提。串口参数(波特率可修改,默认500000):"))
serial_rows = [ serial_rows = [
["波特率", "500000"], ["波特率", "500000(可在下拉框中选择或手动输入)"],
["数据位", "8"], ["数据位", "8"],
["停止位", "1"], ["停止位", "1"],
["校验位", "None(无校验)"], ["校验位", "None(无校验)"],
@@ -302,10 +294,9 @@ para_rows = [
["软件版本", "SoftWare V", "网关固件版本号"], ["软件版本", "SoftWare V", "网关固件版本号"],
["硬件版本", "HardWare V", "网关硬件版本号"], ["硬件版本", "HardWare V", "网关硬件版本号"],
["网关MAC", "GateWay MAC", "网关唯一MAC地址"], ["网关MAC", "GateWay MAC", "网关唯一MAC地址"],
["电池", "Battery", "当前电池电量"], ["电池电量", "Battery", "当前电池电量"],
["注册状态", "Register Status", "0=未注册,1=已注册"], ["注册状态", "MuchReg", "true=已注册,false/null=未注册"],
["通讯方式", "CommInf", "当前通讯方式(Cat1/Eth)"], ["通讯方式", "Much", "当前主通讯方式(eth/cat1/lora)"],
["读取间隔", "CommUnitReadInterval", "通讯单元读取间隔(秒)"],
["Cat1 IMEI", "Cat1 IMEI", "4G模块IMEI号"], ["Cat1 IMEI", "Cat1 IMEI", "4G模块IMEI号"],
["Cat1 SIM", "Cat1 SIM", "SIM卡信息"], ["Cat1 SIM", "Cat1 SIM", "SIM卡信息"],
["Lora频率", "Lora fc", "当前Lora信道频率"], ["Lora频率", "Lora fc", "当前Lora信道频率"],
@@ -313,55 +304,10 @@ para_rows = [
story.append(make_table(["显示项", "指令关键字", "说明"], para_rows, col_widths=[30 * mm, 45 * mm, 75 * mm])) story.append(make_table(["显示项", "指令关键字", "说明"], para_rows, col_widths=[30 * mm, 45 * mm, 75 * mm]))
# 3.3 # 3.3
story.append(heading2("3.3 通讯方式切换")) story.append(heading2("3.3 Lora信道配置"))
story.append(body("网关支持两种通讯方式,可通过快捷按钮切换:"))
comm_rows = [
["Cat1(comm cat1)", "comm cat1", "切换到4G/Cat1通讯模式"],
["ETH(comm eth)", "comm eth", "切换到以太网通讯模式"],
]
story.append(make_table(["按钮", "发送指令", "说明"], comm_rows, col_widths=[40 * mm, 35 * mm, 75 * mm]))
story.append(Spacer(1, 2 * mm))
story.append(note(f"切换通讯方式后,需要重新点击{LQ}查看配置{RQ}刷新当前状态。ETH参数配置必须在ETH模式下进行。"))
story.append(PageBreak())
# 3.4
story.append(heading2("3.4 ETH网络参数配置"))
story.append(body( story.append(body(
f"ETH参数配置区域用于设置网关以太网相关的网络参数。<b>必须先切换到ETH模式</b>才能发送ETH相关指令。" "Lora信道配置用于设置网关的Lora无线通信频率。信道列表覆盖433.1MHz至492.6MHz范围,"
)) "频率间隔0.5MHz,共120个信道可供选择。"
story.append(Spacer(1, 2 * mm))
eth_rows = [
["网关IP", "eth ip", "192.168.1.101", "网关本地IP地址"],
["网关端口", "eth port", "10000", "网关本地端口号(1-65535)"],
["服务器网关", "eth gw", "192.168.1.1", "网络网关地址"],
["服务器IP", "eth dip", "192.168.1.100", "远端服务器IP地址"],
["服务器端口", "eth dport", "8080", "远端服务器端口号(1-65535)"],
]
story.append(make_table(
["参数名称", "指令前缀", "默认值", "说明"],
eth_rows, col_widths=[28 * mm, 25 * mm, 35 * mm, 62 * mm]
))
story.append(Spacer(1, 3 * mm))
story.append(heading3("ETH配置流程"))
eth_steps = [
f"点击<b>ETH(comm eth)</b>按钮切换到以太网模式;",
"在各输入框中填入目标参数值;",
"逐项点击<b>发送</b>按钮,将参数写入网关;",
f"最后点击<b>应用ETH配置(eth apply)</b>按钮,使配置生效。",
]
for i, s in enumerate(eth_steps, 1):
story.append(Paragraph(f"{i}. {s}", style_body))
story.append(Spacer(1, 2 * mm))
story.append(note(f"仅发送参数而未点击{LQ}应用ETH配置{RQ},参数不会生效。IP地址格式为 x.x.x.x,端口范围为1-65535。"))
# 3.5
story.append(heading2("3.5 Lora信道配置"))
story.append(body(
"Lora信道配置用于设置网关的Lora无线通信频率。信道列表覆盖410.1MHz至524.9MHz范围,"
"频率间隔0.2MHz,共575个信道可供选择。"
)) ))
story.append(Spacer(1, 2 * mm)) story.append(Spacer(1, 2 * mm))
@@ -375,32 +321,15 @@ for i, s in enumerate(lora_steps, 1):
story.append(Spacer(1, 2 * mm)) story.append(Spacer(1, 2 * mm))
lora_range = [ lora_range = [
["起始频率", "410.1 MHz(信道1)"], ["起始频率", "433.1 MHz(信道0)"],
["终止频率", "524.9 MHz(信道575)"], ["终止频率", "492.6 MHz(信道119)"],
["频率间隔", "0.2 MHz(200 kHz)"], ["频率间隔", "0.5 MHz(500 kHz)"],
["指令格式", "lora fc [频率值,单位Hz]"], ["指令格式", "lora fc [频率值,单位Hz]"],
] ]
story.append(make_table(["项目", "值"], lora_range, col_widths=[35 * mm, 115 * mm])) story.append(make_table(["项目", "值"], lora_range, col_widths=[35 * mm, 115 * mm]))
# 3.6 # 3.4
story.append(heading2("3.6 低功耗配置")) story.append(heading2("3.4 自定义指令"))
story.append(body(
f"低功耗配置显示网关的低功耗运行参数,这些参数通过{LQ}查看配置(para){RQ}指令读取,在{LQ}低功耗配置{RQ}分组框中显示:"
))
lp_rows = [
["采集间隔(CI)", "传感器数据采集的时间间隔"],
["上报间隔(RI)", "正常数据上报的时间间隔"],
["紧急上报间隔(EI)", "紧急状态下的数据上报间隔"],
["紧急上报时长(ET)", "紧急上报模式持续时长"],
]
story.append(make_table(["参数", "说明"], lp_rows, col_widths=[40 * mm, 110 * mm]))
story.append(Spacer(1, 2 * mm))
story.append(note("低功耗参数单位可能为秒(sec)或分钟(min),以网关返回的实际值为准。"))
story.append(PageBreak())
# 3.7
story.append(heading2("3.7 自定义指令"))
story.append(body( story.append(body(
"自定义指令区域允许用户手动输入任意指令与网关交互,适用于高级调试和特殊配置场景。" "自定义指令区域允许用户手动输入任意指令与网关交互,适用于高级调试和特殊配置场景。"
)) ))
@@ -414,17 +343,30 @@ custom_steps = [
for i, s in enumerate(custom_steps, 1): for i, s in enumerate(custom_steps, 1):
story.append(Paragraph(f"{i}. {s}", style_body)) story.append(Paragraph(f"{i}. {s}", style_body))
# 3.8 # 3.5
story.append(heading2("3.8 通讯日志")) story.append(heading2("3.5 通讯日志"))
story.append(body("通讯日志区域实时显示与网关的通讯数据,采用深色背景方便阅读:")) story.append(body("通讯日志区域实时显示与网关的通讯数据,采用深色背景方便阅读:"))
log_rows = [ log_rows = [
["蓝色文字", "发送的指令(&gt;&gt;&gt; 前缀)", "包含时间戳和发送的指令内容"], ["蓝色文字", "发送的指令(&gt;&gt;&gt; 前缀)", "发送的指令内容"],
["橙色文字", "接收的数据(&lt;&lt;&lt; 前缀)", "包含时间戳和网关返回的原始数据"], ["橙色文字", "接收的数据(&lt;&lt;&lt; 前缀)", "网关返回的原始数据"],
] ]
story.append(make_table(["颜色", "类型", "说明"], log_rows, col_widths=[25 * mm, 45 * mm, 80 * mm])) story.append(make_table(["颜色", "类型", "说明"], log_rows, col_widths=[25 * mm, 45 * mm, 80 * mm]))
story.append(Spacer(1, 2 * mm)) story.append(Spacer(1, 2 * mm))
story.append(note(f"点击{LQ}清空日志{RQ}按钮可清除当前日志内容。日志数据仅保留在内存中,关闭软件后自动清除。")) story.append(note(f"点击{LQ}清空日志{RQ}按钮可清除当前日志内容。日志数据仅保留在内存中,关闭软件后自动清除。"))
story.append(heading2("3.6 设备管理"))
story.append(body(
f"点击{LQ}设备管理{RQ}按钮打开设备管理对话框,可查询与删除网关已注册的通讯单元设备:"
))
mgmt_rows = [
[f"{LQ}刷新查询{RQ}", "向网关发送查询指令,列出全部已注册设备(类型、MAC、传感器类型、电量、RSSI/SNR、在线状态等)"],
[f"{LQ}删除{RQ}", "点击设备行右侧的删除按钮并确认后,立即从列表中移除该设备(列表即时更新,无需重新查询)"],
[f"{LQ}删除全部设备{RQ}", "删除网关上全部已注册设备,列表清空"],
]
story.append(make_table(["操作", "说明"], mgmt_rows, col_widths=[35 * mm, 115 * mm]))
story.append(Spacer(1, 2 * mm))
story.append(note("删除操作直接生效,若需确认设备列表最新状态,可再次点击刷新查询。"))
story.append(PageBreak()) story.append(PageBreak())
# ==================== 4. 指令参考 ==================== # ==================== 4. 指令参考 ====================
@@ -435,15 +377,10 @@ story.append(Spacer(1, 2 * mm))
cmd_rows = [ cmd_rows = [
["para", "读取网关全部配置参数", "查看配置(para)"], ["para", "读取网关全部配置参数", "查看配置(para)"],
["comm cat1", "切换到Cat1通讯模式", "Cat1(comm cat1)"],
["comm eth", "切换到ETH通讯模式", "ETH(comm eth)"],
["eth ip &lt;IP&gt;", "设置网关本地IP地址", "ETH参数配置"],
["eth port &lt;端口&gt;", "设置网关本地端口号", "ETH参数配置"],
["eth gw &lt;IP&gt;", "设置网络网关地址", "ETH参数配置"],
["eth dip &lt;IP&gt;", "设置远端服务器IP", "ETH参数配置"],
["eth dport &lt;端口&gt;", "设置远端服务器端口", "ETH参数配置"],
["eth apply", "应用ETH配置参数", "应用ETH配置"],
["lora fc &lt;频率&gt;", "设置Lora信道频率(Hz)", "Lora信道配置"], ["lora fc &lt;频率&gt;", "设置Lora信道频率(Hz)", "Lora信道配置"],
["culs", "查询已注册通讯单元设备列表", "设备管理-刷新查询"],
["cudel &lt;MAC&gt;", "删除指定MAC的通讯单元设备", "设备管理-删除"],
["cudel all", "删除全部已注册通讯单元设备", "设备管理-删除全部设备"],
] ]
story.append(make_table( story.append(make_table(
["指令格式", "功能说明", "对应操作"], ["指令格式", "功能说明", "对应操作"],
@@ -465,18 +402,15 @@ faqs = [
("Q2:发送指令后没有回复?", ("Q2:发送指令后没有回复?",
f"1)确认串口已成功打开(按钮显示红色{LQ}关闭串口{RQ});2)检查串口线RX/TX是否接反;" f"1)确认串口已成功打开(按钮显示红色{LQ}关闭串口{RQ});2)检查串口线RX/TX是否接反;"
"3)确认网关设备已上电且正常运行;4)尝试重新连接串口。"), "3)确认网关设备已上电且正常运行;4)尝试重新连接串口。"),
(f"Q3:ETH参数发送提示{LQ}请先切换到ETH模式{RQ}?", ("Q3:Lora信道列表很长,如何快速选择?",
f"ETH参数配置必须在ETH通讯模式下进行。请先点击{LQ}ETH(comm eth){RQ}按钮切换模式,"
"再进行ETH参数的发送。"),
("Q4:修改ETH参数后未生效?",
f"修改ETH参数后,需要点击{LQ}应用ETH配置(eth apply){RQ}按钮,参数才会真正生效。"
"仅发送参数值而未应用,网关不会更新配置。"),
("Q5:如何确认当前网关的通讯方式?",
f"点击{LQ}查看配置(para){RQ}按钮,在{LQ}当前网关配置{RQ}区域查看{LQ}通讯方式{RQ}字段,"
"显示为Cat1或Eth。"),
("Q6:Lora信道列表很长,如何快速选择?",
"Lora信道下拉框支持鼠标滚轮滚动选择,也可以点击下拉后直接滚动浏览。" "Lora信道下拉框支持鼠标滚轮滚动选择,也可以点击下拉后直接滚动浏览。"
"信道编号和频率一一对应,选择后点击发送即可。"), "信道编号和频率一一对应,选择后点击发送即可。"),
("Q4:设备升级失败或超时怎么办?",
f"1)确认升级过程中串口连接稳定,未出现断开或断电;2)确认{LQ}设备MAC{RQ}与目标网关一致;"
"3)确认固件文件为与设备匹配的.bin文件且未损坏;4)重新点击开始升级再试一次。"),
("Q5:设备管理查询不到设备或列表为空?",
"1)确认网关上已注册过通讯单元设备;2)点击刷新查询重试;"
"3)确认串口通讯正常(可通过查看配置验证);4)若刚删除设备,列表已实时更新属正常现象。"),
] ]
for q, a in faqs: for q, a in faqs:
story.append(Paragraph(f"<b>{q}</b>", style_body)) story.append(Paragraph(f"<b>{q}</b>", style_body))
@@ -490,14 +424,13 @@ story.append(heading1("6 注意事项"))
story.append(hr()) story.append(hr())
warnings = [ warnings = [
"<b>通讯模式切换:</b>在进行ETH参数配置前,务必先切换到ETH通讯模式,否则参数发送将被拒绝。",
f"<b>ETH配置应用:</b>修改ETH网络参数后,必须点击{LQ}应用ETH配置{RQ}按钮才能使参数生效。",
"<b>串口独占:</b>同一串口在同一时间只能被一个程序打开,请确保使用前关闭其他可能占用串口的软件。", "<b>串口独占:</b>同一串口在同一时间只能被一个程序打开,请确保使用前关闭其他可能占用串口的软件。",
"<b>波特率固定:</b>本工具固定使用500000波特率,与网关固件匹配,请勿尝试修改。", "<b>波特率:</b>波特率默认500000,可在下拉框选择或手动输入修改,请与网关固件实际波特率保持一致。",
"<b>参数格式:</b>IP地址需符合标准格式(如192.168.1.101),端口号范围为1-65535,否则发送可能失败。",
f"<b>数据备份:</b>修改配置前建议先通过{LQ}查看配置{RQ}记录当前参数,以便需要时恢复。", f"<b>数据备份:</b>修改配置前建议先通过{LQ}查看配置{RQ}记录当前参数,以便需要时恢复。",
"<b>断电保护:</b>配置修改后请确认网关已保存参数,避免在参数写入过程中断电。", "<b>断电保护:</b>配置修改后请确认网关已保存参数,避免在参数写入过程中断电。",
"<b>驱动安装:</b>首次使用需安装USB转串口芯片驱动(如CH340、CP2102等),具体型号取决于使用的转接线。", "<b>驱动安装:</b>首次使用需安装USB转串口芯片驱动(如CH340、CP2102等),具体型号取决于使用的转接线。",
"<b>升级安全:</b>固件升级过程中严禁断开串口、关闭软件或给网关断电,否则可能导致网关固件损坏而无法启动。",
"<b>固件匹配:</b>请使用与网关硬件和项目匹配的固件文件(.bin),错误的固件可能导致设备功能异常。",
] ]
for w in warnings: for w in warnings:
story.append(Paragraph(f"\u26a0 {w}", style_body)) story.append(Paragraph(f"\u26a0 {w}", style_body))
Binary file not shown.