Files
weinviews-unitouch/界面开发记录.md
T
admin edba84fd8b 更新工程与开发文档:多线共屏方案落地
- 工程(unitouch.cmtp/bak/debug/XD)与素材更新:13 号窗「系统IP和关于」、登录提示内联、A/B 切换等
- 新增素材:ai-button.png、ai-button-disabled.png、600x500.png、组 1985/1986.png
- 文档:新增第 9 章多线共屏完整流程、5.9 拓扑图、4.1 三级权限总表;
  13 号窗字段与 net-read/net-apply/line-ab-switch 宏;全部宏逐行注释;
  登录提示内联、角色防呆(角色=0 不连制丸)、默认 A 线、换屏流程等
2026-09-25 22:46:58 +08:00

111 KiB
Raw Blame History

JOYEA 触摸屏开发记录(CMT2167X / EasyBuilder Pro 6.10)

工程目录:E:\AiCodeGitea\weinveiw-touch | 远程:http://124.220.63.9:30000/admin/weinviews-unitouch.git

1. 目标与环境

  • 硬件:WEINTEK CMT2167X,1920×1080,arm64,24VDC。
  • 软件:EasyBuilder Pro 6.10.02.421。
  • 通讯/设备:与现有 unitouch 工程一致(不改 PLC 与地址映射)。

2. 工程资产

文件 说明
unitouch.cmtp(+ bak1~3) EB Pro 工程文件
unitouch.debug 调试数据
lwaddr.xls / lwaddr.csv 系统地址大全(1429 条,查系统寄存器用)
制丸线HMI界面UniTouch布局202500915.xd XD 设计源(18 画板)

unitouch.cxob 是编译输出,已加入 .gitignore,不再入库。

XD 画板:A_start、B_PID、C_运行1~3、D_点动1/2、E_配方1~3、F_设备参数2、G_清洗主机1、H_报警1、I_数据1~3、J_关于。

3. 窗口结构

窗口 名称 用途
4 — 白色底窗口(层叠底层)
5~9 — EB Pro 系统窗口(Device Response / HMI Connection / Password Restriction / Storage / Backup),不改内容
10 登陆和入口画面 登录 + 入口;窗口开启执行 logout
11 弹出 无权限提示弹窗(600×300,位 LB-110)
12 修改时间 修改时间弹窗(位 LB-120)
13 系统IP和关于 关于 + 设置(1200×800 弹窗,位 LB-123)
14 用户管理 用户管理弹窗(位 LB-122,开启宏 usr-init)
15 用户删除确认 删除确认弹窗(位 LB-121)
16 空页面弹窗 无内容提示弹窗(600×300,位 LB-124)
17 超管登陆 管理员登录弹窗(600×360,位 LB-125)
20 点检check 点检画面(可退出,暂无功能)
100 common-window-joyea 公共窗口(顶部栏/导航/常驻元件)
101~128 X1Y1 ~ X4Y7 主画面,共 28 个

4. 命名与导航

  • 画面名:X{段}Y{功能}_{工艺段}_{功能}
  • X(工艺段):X1 配料/上料、X2 上料清洗、X3 制丸、X4 制丸清洗
  • Y(功能):Y1 初始状态、Y2 运行、Y3 配方、Y4 设备参数、Y5 I/O监控、Y6 报警、Y7 数据趋势
  • 窗口号:WIN = 101 + (X-1)*7 + (Y-1)(101~128)
  • 导航机制:按钮宏把 X/Y 与目标窗口号写入 LW-0/1/2,由 PLC 控制元件「切换基本窗口」(触发地址 LW-2,回写 LW-3)执行切窗。
  • 顶部「运行/点动」为全局状态,暂不参与 X/Y 导航。

4.1 权限分配总表(按用户组)

权限分三级:● 拥有权限(可操作)、○ 仅可见(看得到,点了弹 11 号无权限窗)、× 不可见(直接隐藏,看不到)。 类别:A=工程师、B=管理员、C=上料、D=制丸;内定 admin 权限全开,所以整行都是 ●(对 × 的元件它也一样可见可操作)。 未登录时类别位为 0,所有受限项按无权限处理。

功能 / 页面 工程师 A 管理员 B 上料 C 制丸 D admin
登录 / 注销(10 号登录页) ● ● ● ● ●
顶部当前账户 + 长按 1s 注销 ● ● ● ● ●
点检页(20 号,入口 + 下一步) ● ● ● ● ●
导航 X1 配料/上料 ● ● ● ○ ●
导航 X2 上料清洗 ● ● ● ○ ●
导航 X3 制丸 ● ● ○ ● ●
导航 X4 制丸清洗 ● ● ○ ● ●
导航 Y1 初始状态 ● ● ● ● ●
导航 Y2 运行 ● ● ● ● ●
导航 Y3 配方 ● ● ● ● ●
导航 Y4 设备参数 ● ● ○ ○ ●
导航 Y5 I/O 监控 ● ● ○ ○ ●
导航 Y6 报警(含列表/确认) ● ● ● ● ●
导航 Y7 数据趋势 ● ● ● ● ●
用户管理(14 号窗:新增/删除/改权限/改密码) × ● × × ●
修改时间(12 号窗) × ● × × ●
关于(13 号窗) ● × × × ●
X1Y1 节点状态人工修正(数值输入) ● × × × ●
无权限提示(11 号窗)/ 无内容提示(16 号窗) 系统自动 系统自动 系统自动 系统自动 系统自动

三级怎么在 EB Pro 里设置

级别 元件设置方法
● 拥有权限 元件不勾安全,照旧由宏按 LW-9222 放行(现状)
○ 仅可见 元件不勾安全,宏校验不通过 → 弹 11 号无权限窗,元件保持可见(现状)
× 不可见 元件「安全」页:勾「启用」→ 操作类别只勾有权组 → 勾「无权限时隐藏」

× 一共 4 处、6 个元件:用户管理按钮、时钟上的透明按钮(修改时间)、关于按钮、X1Y1 的节点修正数值输入框(两组各 3 个)。 若希望「仅可见」点了不弹提示,把对应宏里 LB-110 那一行删掉即可(变成纯无反应)。

5. 地址规划

5.1 导航 / 公共

地址 用途
LW-0 X(1~4)
LW-1 Y(1~7)
LW-2 目标窗口号(PLC 控制元件触发地址)
LW-3 当前窗口号(PLC 控制元件回写)
LW-70~73 页面存在位图:bit0=Y1 … bit6=Y7,1=该页有内容;X1/X2/X3/X4 段分别对应 LW-70/71/72/73
LW-9222 当前登录用户类别位(bit0=A 工程师 / bit1=B 管理员 / bit2=C 上料 / bit3=D 制丸;0=未登录)

5.2 弹窗 / 状态位

位 用途
LB-110 无权限提示弹窗(→ 11 号窗)
LB-120 修改时间弹窗(→ 12 号窗)
LB-121 删除确认弹窗(→ 15 号窗)
LB-122 用户管理弹窗(→ 14 号窗)
LB-123 关于弹窗(→ 13 号窗)
LB-124 无内容提示弹窗(→ 16 号窗)
LB-125 管理员登录弹窗(→ 17 号窗)
LB-131 三个 PLC 联机汇总(触发条件 AND → 位状态指示灯)

5.3 控制地址(高级安全模式)

地址 用途
LW-100 命令(见 5.4)
LW-101 命令结果(结果码见 5.5)
LW-102 用户索引(列表选中 / 读剩余时间的结果)
LW-103 用户权限位(bit0=A / bit1=B / bit2=C / bit3=D)
LW-104~111 用户名(ASCII,8 words)
LW-112~119 密码(ASCII,8 words)

5.4 LW-100 命令表

值 命令 配合地址
1 / 2 名称登入 / 索引登入 LW-104+LW-112 / LW-102+LW-112
3 注销 —
4 改当前账号密码 名=原密码、密=新密码
5 新增 LW-104+LW-112+LW-103
6 / 15 新增临时账号(分/天) 上述 + LW-102=有效期(0=永久)
7 / 8 名称删除 / 索引删除 LW-104 / LW-102
9 / 10 名称设权限 / 索引设权限 +LW-103
11 / 12 名称设密码 / 索引设密码 +LW-112
13 / 14 名称读权限 / 索引读权限 结果→LW-103
16 / 17 新增限期账号(分/天) 有效期不得为 0
18~21 读剩余分钟/天数 结果→LW-102

5.5 LW-101 结果码

Hex 含义 排查
01 成功 —
02 错误命令 LW-100 值无效
04 账户已存在 LW-104 重名
08 账户不存在 LW-104/LW-102 对不上
10 密码错误 LW-112 内容
20 命令无法执行 删当前账号 / admin
40 名称不合法 LW-104
80 密码含无效字符 LW-112
100 / 200 导入无效 / 超期 U 盘 / 安全密钥

5.6 提示 / 点检 / 时间

地址 用途
LW-261 登录提示字符串表编号(0 空 / 1 成功 / 2 不存在 / 3 密码错 / 4 无法执行 / 5 名称不合法 / 6 密码无效 / 7 登录失败)
LW-9017~9022 RTC:秒/分/时/日/月/年(写入即设置时间)
LW-40~45 改时间暂存:秒/分/时/日/月/年

5.7 网络 / 版本(关于页)

项目 地址 元件
HMI IP LW-9129~9132 数值 ×4(16-bit,3 位)
子网掩码 LW-10750~10753 数值 ×4
网关 LW-9125~9128 数值 ×4
MAC LW-9135(6 words) 字符元件,字长 6
以太网端口 LW-9133 数值
DNS LW-10804~10807 数值 ×4
连接速度 LW-10815 数值(0 失败/10/100/1000)
DHCP LW-10812 数值(0 关/1 开)
程序版本 LW-9120 数值 32-bit
OS 版本 LW-9026/9027/9028 数值(年/月/日)
工程名称 LW-9100(14 words) 字符,字长 14
工程编译日期 LW-9122/9123/9124 数值(年/月/日)
HMI 名称 LW-10884(16 words) 字符,字长 16
HMI 机型 ID LW-9054 数值 32-bit
HMI 识别码 LW-9046 数值 32-bit
工程文件大小 LW-9118 数值 32-bit(K bytes)
工程校验和 LW-12752(16 words) 字符,字长 16

网络写回控制位:LB-12041 刷新、LB-12094/LB-12095 更新(写 IP/掩码/网关后置 ON)、LB-12822 桥接模式。

5.8 通用程序:3 台 PLC / 多屏通用配置

一套工程,上料屏 / 制丸A屏 / 制丸B屏通用;换屏、换 PLC 只在 13 号窗改配置,现场不需要 EasyBuilder。

工程里的设备划分(两个以太网设备槽)

设备 绑定内容 上料屏 制丸A屏 制丸B屏
设备4:上料 PLC X1/X2 画面、C 组数据 上料 PLC 的 IP 上料 PLC 的 IP 上料 PLC 的 IP
设备5:制丸 PLC X3/X4 画面、D 组数据 A 或 B(D 登录后切) A 的 IP B 的 IP
  • 制丸A屏、制丸B屏各自只连「自己的 PLC + 上料 PLC」,互相看不见;
  • 上料屏 D 登录:用「A/B 切换」按钮在 设备5 的 IP 间切(同一时间看一条线);默认 A,只有人工切换才到 B,重启回 A;按钮暂放 13 号窗(能开 13 号窗的是工程师 A / admin),后续再移到制丸页给 D 用;
  • 换屏 / 换 PLC:备件装同程序 → 13 号窗填 IP/角色 → 点「应用」即可。

13 号窗字段(全部存 RW,掉电保持)

字段 地址 写入的系统寄存器
HMI IP / 掩码 / 网关 RW-20~31 LW-91299132 / LW-1075010753 / LW-9125~9128 + LB-12094(备屏/换屏用;HMI 固定 IP 必填)
上料 PLC IP RW-40~43 LW-9600~9603(设备4) + LB-10070
制丸A PLC IP RW-44~47 选 A 时 → LW-9605~9608(设备5) + LB-10071
制丸B PLC IP RW-48~51 选 B 时 → LW-9605~9608(设备5) + LB-10071
本机角色 RW-60:1=上料屏 / 2=制丸A屏 / 3=制丸B屏 角色 2/3 自动锁线别;角色=0 不连制丸设备(防误连)
制丸线选择 RW-61:1=A 线 / 2=B 线 上料屏手动选;制丸屏由角色决定

设备槽号按你工程实际(以太网设备从 4 号起):设备5 → LW-9605/LB-10071,设备6 → LW-9610/LB-10072。设备号查看:EB Pro 设备列表,或显示 LW-9300~9306(0=该槽无设备)。

切换互锁(防旧数据/防误操作)

用途 地址 说明
设备4 通讯状态 LB-10100 上料 PLC:ON=已连接;写 ON 可重连一次
设备5 通讯状态 LB-10400 制丸 PLC:ON=已连接;写 ON 可重连一次
错误码 LW-9403 / LW-9404 设备4 / 设备5
禁止系统弹窗 LB-11963 / LB-11964 置 ON:不弹设备4/5 的 “Device No Response”
  • 制丸页放「通讯正常」指示灯 = LB-10400;A/B 切换后(或刚上电)等它变 ON 再操作,避免按到旧画面数据;
  • 切换按钮(line-ab-switch)只负责切 IP 和重连,不等待;互锁交给指示灯/操作条件。
  • A/B 切换按钮只在上料屏(RW-60=1)生效:制丸A/B 屏线别由角色锁死,宏内已挡,admin 登录也切不动。

5.9 拓扑图

三台 PLC、三台 HMI 都接在 PN 交换机(同一网段);图中画的是逻辑连接(每台 HMI 只连它需要的那两台 PLC)。

graph LR
  P1["S7-1500<br/>上料/配料 PLC"]
  PA["S7-1500<br/>制丸A PLC"]
  PB["S7-1500<br/>制丸B PLC"]
  H1["HMI 上料屏<br/>role=1"]
  H2["HMI 制丸A屏<br/>role=2"]
  H3["HMI 制丸B屏<br/>role=3"]
  H1 -- "设备4" --> P1
  H1 -. "设备5(A/B 可切)" .-> PA
  H1 -.-> PB
  H2 -- "设备4" --> P1
  H2 -- "设备5" --> PA
  H3 -- "设备4" --> P1
  H3 -- "设备5" --> PB

说明

  • 一套工程烧 3 台屏;装机时在 13 号窗设:本机角色 RW-60、线别 RW-61、上料 IP RW-40~43、制丸A IP RW-44~47、制丸B IP RW-48~51 → 点「应用」;
  • 用户组:C(上料)→ X1/X2(绑 设备4);D(制丸)→ X3/X4(绑 设备5);admin 全开;
  • 制丸A屏只连「A 的 PLC + 上料 PLC」;制丸B屏只连「B 的 PLC + 上料 PLC」;A/B 互不可见;
  • 上料屏 D 登录后可用「A/B 切换」按钮切换 设备5 指向(仅 role=1 生效);按钮暂放 13 号窗,后续再移到制丸页。

6. 宏清单

命名:导航 = btn-*、登录 = main-init/btn-login-index/logout、管理员登录 = admin-open/admin-login/admin-close、用户管理 = user-open + usr-*(你的工程删除类为 usr-delete*)、关于窗 = about-open/about-close、时间 = open-time/set-time/close-time、弹窗 = close-msg/close-nopage。 登录提示已内联:LW-101 结果码 → LW-261 的映射直接写在 btn-login-index/admin-login 内,零延迟、不轮询、不依赖宏编号。net-apply 用宏属性「当 HMI 启动时即执行一次」。 权限判定统一用 LW-9222 的类别位(GETBIT 取值 + if,不用 and/&)。

main-init(开机执行一次)

macro_command main()                                      // 宏入口:HMI 启动时执行一次
    short x = 1                                           // X 初值 = 1
    short y = 1                                           // Y 初值 = 1
    short win = 0                                         // 目标窗口号初值 = 0(不切窗)
    short r1 = 127                                        // X1 段存在位图:bit0=Y1…bit6=Y7,1=有内容(按实际改)
    short r2 = 127                                        // X2 段存在位图(按实际改)
    short r3 = 127                                        // X3 段存在位图(按实际改)
    short r4 = 127                                        // X4 段存在位图(按实际改)
    short one = 1                                         // 常量 1(默认制丸线别)
    SetData(x, "Local HMI", LW, 0, 1)                     // X → LW-0
    SetData(y, "Local HMI", LW, 1, 1)                     // Y → LW-1
    SetData(win, "Local HMI", LW, 2, 1)                   // 窗口号 0 → LW-2(不触发切窗)
    SetData(r1, "Local HMI", LW, 70, 1)                   // X1 位图 → LW-70
    SetData(r2, "Local HMI", LW, 71, 1)                   // X2 位图 → LW-71
    SetData(r3, "Local HMI", LW, 72, 1)                   // X3 位图 → LW-72
    SetData(r4, "Local HMI", LW, 73, 1)                   // X4 位图 → LW-73
    SetData(one, "Local HMI", RW, 61, 1)                    // 开机默认制丸线别 = A(只有人工切换才变 B)
end macro_command                                         // 宏结束

导航宏说明:先查权限(LW-9222 类别位)→ 再查当前 Y 页面是否存在(LW-70~73 位图)→ 有内容才切窗,无内容弹 16 号窗。

btn-x1(X1 配料/上料 · 权限 A/B/C)

macro_command main()                                      // 宏入口:按下 X1 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 1                                           // 本按钮固定切到 X=1
    short y = 0                                           // 运行时读回的当前 Y(1~7)
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // X1 段页面存在位图(LW-70)
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bC, 2)                                    // 取 bit2 → bC(C 上料)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bC == 1 then                                       // 含类别 C?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 继续查页面
        GetData(y, "Local HMI", LW, 1, 1)                 // 读当前 Y → y
        GetData(vpm, "Local HMI", LW, 70, 1)              // 读 X1 段存在位图 → vpm
        if y == 1 then                                    // 当前 Y=1?
            GETBIT(vpm, bv, 0)                            // 取位 0(Y1)→ bv
        end if                                            // 结束判断
        if y == 2 then                                    // 当前 Y=2?
            GETBIT(vpm, bv, 1)                            // 取位 1(Y2)→ bv
        end if                                            // 结束判断
        if y == 3 then                                    // 当前 Y=3?
            GETBIT(vpm, bv, 2)                            // 取位 2(Y3)→ bv
        end if                                            // 结束判断
        if y == 4 then                                    // 当前 Y=4?
            GETBIT(vpm, bv, 3)                            // 取位 3(Y4)→ bv
        end if                                            // 结束判断
        if y == 5 then                                    // 当前 Y=5?
            GETBIT(vpm, bv, 4)                            // 取位 4(Y5)→ bv
        end if                                            // 结束判断
        if y == 6 then                                    // 当前 Y=6?
            GETBIT(vpm, bv, 5)                            // 取位 5(Y6)→ bv
        end if                                            // 结束判断
        if y == 7 then                                    // 当前 Y=7?
            GETBIT(vpm, bv, 6)                            // 取位 6(Y7)→ bv
        end if                                            // 结束判断
        if bv == 1 then                                   // 该页面有内容?
            SetData(x, "Local HMI", LW, 0, 1)             // X=1 → LW-0
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-x2(X2 上料清洗 · 权限 A/B/C)

macro_command main()                                      // 宏入口:按下 X2 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 2                                           // 本按钮固定切到 X=2
    short y = 0                                           // 运行时读回的当前 Y(1~7)
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // X2 段页面存在位图(LW-71)
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bC, 2)                                    // 取 bit2 → bC(C 上料)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bC == 1 then                                       // 含类别 C?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 继续查页面
        GetData(y, "Local HMI", LW, 1, 1)                 // 读当前 Y → y
        GetData(vpm, "Local HMI", LW, 71, 1)              // 读 X2 段存在位图 → vpm
        if y == 1 then                                    // 当前 Y=1?
            GETBIT(vpm, bv, 0)                            // 取位 0(Y1)→ bv
        end if                                            // 结束判断
        if y == 2 then                                    // 当前 Y=2?
            GETBIT(vpm, bv, 1)                            // 取位 1(Y2)→ bv
        end if                                            // 结束判断
        if y == 3 then                                    // 当前 Y=3?
            GETBIT(vpm, bv, 2)                            // 取位 2(Y3)→ bv
        end if                                            // 结束判断
        if y == 4 then                                    // 当前 Y=4?
            GETBIT(vpm, bv, 3)                            // 取位 3(Y4)→ bv
        end if                                            // 结束判断
        if y == 5 then                                    // 当前 Y=5?
            GETBIT(vpm, bv, 4)                            // 取位 4(Y5)→ bv
        end if                                            // 结束判断
        if y == 6 then                                    // 当前 Y=6?
            GETBIT(vpm, bv, 5)                            // 取位 5(Y6)→ bv
        end if                                            // 结束判断
        if y == 7 then                                    // 当前 Y=7?
            GETBIT(vpm, bv, 6)                            // 取位 6(Y7)→ bv
        end if                                            // 结束判断
        if bv == 1 then                                   // 该页面有内容?
            SetData(x, "Local HMI", LW, 0, 1)             // X=2 → LW-0
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-x3(X3 制丸 · 权限 A/B/D)

macro_command main()                                      // 宏入口:按下 X3 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 3                                           // 本按钮固定切到 X=3
    short y = 0                                           // 运行时读回的当前 Y(1~7)
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // X3 段页面存在位图(LW-72)
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bD = 0                                          // 类别 D 位(制丸)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bD, 3)                                    // 取 bit3 → bD(D 制丸)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bD == 1 then                                       // 含类别 D?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 继续查页面
        GetData(y, "Local HMI", LW, 1, 1)                 // 读当前 Y → y
        GetData(vpm, "Local HMI", LW, 72, 1)              // 读 X3 段存在位图 → vpm
        if y == 1 then                                    // 当前 Y=1?
            GETBIT(vpm, bv, 0)                            // 取位 0(Y1)→ bv
        end if                                            // 结束判断
        if y == 2 then                                    // 当前 Y=2?
            GETBIT(vpm, bv, 1)                            // 取位 1(Y2)→ bv
        end if                                            // 结束判断
        if y == 3 then                                    // 当前 Y=3?
            GETBIT(vpm, bv, 2)                            // 取位 2(Y3)→ bv
        end if                                            // 结束判断
        if y == 4 then                                    // 当前 Y=4?
            GETBIT(vpm, bv, 3)                            // 取位 3(Y4)→ bv
        end if                                            // 结束判断
        if y == 5 then                                    // 当前 Y=5?
            GETBIT(vpm, bv, 4)                            // 取位 4(Y5)→ bv
        end if                                            // 结束判断
        if y == 6 then                                    // 当前 Y=6?
            GETBIT(vpm, bv, 5)                            // 取位 5(Y6)→ bv
        end if                                            // 结束判断
        if y == 7 then                                    // 当前 Y=7?
            GETBIT(vpm, bv, 6)                            // 取位 6(Y7)→ bv
        end if                                            // 结束判断
        if bv == 1 then                                   // 该页面有内容?
            SetData(x, "Local HMI", LW, 0, 1)             // X=3 → LW-0
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-x4(X4 制丸清洗 · 权限 A/B/D)

macro_command main()                                      // 宏入口:按下 X4 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 4                                           // 本按钮固定切到 X=4
    short y = 0                                           // 运行时读回的当前 Y(1~7)
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // X4 段页面存在位图(LW-73)
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bD = 0                                          // 类别 D 位(制丸)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bD, 3)                                    // 取 bit3 → bD(D 制丸)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bD == 1 then                                       // 含类别 D?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 继续查页面
        GetData(y, "Local HMI", LW, 1, 1)                 // 读当前 Y → y
        GetData(vpm, "Local HMI", LW, 73, 1)              // 读 X4 段存在位图 → vpm
        if y == 1 then                                    // 当前 Y=1?
            GETBIT(vpm, bv, 0)                            // 取位 0(Y1)→ bv
        end if                                            // 结束判断
        if y == 2 then                                    // 当前 Y=2?
            GETBIT(vpm, bv, 1)                            // 取位 1(Y2)→ bv
        end if                                            // 结束判断
        if y == 3 then                                    // 当前 Y=3?
            GETBIT(vpm, bv, 2)                            // 取位 2(Y3)→ bv
        end if                                            // 结束判断
        if y == 4 then                                    // 当前 Y=4?
            GETBIT(vpm, bv, 3)                            // 取位 3(Y4)→ bv
        end if                                            // 结束判断
        if y == 5 then                                    // 当前 Y=5?
            GETBIT(vpm, bv, 4)                            // 取位 4(Y5)→ bv
        end if                                            // 结束判断
        if y == 6 then                                    // 当前 Y=6?
            GETBIT(vpm, bv, 5)                            // 取位 5(Y6)→ bv
        end if                                            // 结束判断
        if y == 7 then                                    // 当前 Y=7?
            GETBIT(vpm, bv, 6)                            // 取位 6(Y7)→ bv
        end if                                            // 结束判断
        if bv == 1 then                                   // 该页面有内容?
            SetData(x, "Local HMI", LW, 0, 1)             // X=4 → LW-0
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-y1(Y1 初始状态 · 权限 A/B/C/D)

macro_command main()                                      // 宏入口:按下 Y1 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 0                                           // 运行时读回的当前 X(1~4)
    short y = 1                                           // 本按钮固定切到 Y=1
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // 当前 X 段页面存在位图
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    short bD = 0                                          // 类别 D 位(制丸)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bC, 2)                                    // 取 bit2 → bC(C 上料)
    GETBIT(cls, bD, 3)                                    // 取 bit3 → bD(D 制丸)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bC == 1 then                                       // 含类别 C?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bD == 1 then                                       // 含类别 D?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 查当前 X 段位图
        GetData(x, "Local HMI", LW, 0, 1)                 // 读当前 X → x
        if x == 1 then                                    // 当前在 X1 段?
            GetData(vpm, "Local HMI", LW, 70, 1)          // 读 X1 段位图 LW-70
        end if                                            // 结束判断
        if x == 2 then                                    // 当前在 X2 段?
            GetData(vpm, "Local HMI", LW, 71, 1)          // 读 X2 段位图 LW-71
        end if                                            // 结束判断
        if x == 3 then                                    // 当前在 X3 段?
            GetData(vpm, "Local HMI", LW, 72, 1)          // 读 X3 段位图 LW-72
        end if                                            // 结束判断
        if x == 4 then                                    // 当前在 X4 段?
            GetData(vpm, "Local HMI", LW, 73, 1)          // 读 X4 段位图 LW-73
        end if                                            // 结束判断
        GETBIT(vpm, bv, 0)                                // 取位 0(Y1)→ bv
        if bv == 1 then                                   // 该页面有内容?
            SetData(y, "Local HMI", LW, 1, 1)             // Y=1 → LW-1
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-y2(Y2 运行 · 权限 A/B/C/D)

macro_command main()                                      // 宏入口:按下 Y2 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 0                                           // 运行时读回的当前 X(1~4)
    short y = 2                                           // 本按钮固定切到 Y=2
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // 当前 X 段页面存在位图
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    short bD = 0                                          // 类别 D 位(制丸)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bC, 2)                                    // 取 bit2 → bC(C 上料)
    GETBIT(cls, bD, 3)                                    // 取 bit3 → bD(D 制丸)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bC == 1 then                                       // 含类别 C?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bD == 1 then                                       // 含类别 D?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 查当前 X 段位图
        GetData(x, "Local HMI", LW, 0, 1)                 // 读当前 X → x
        if x == 1 then                                    // 当前在 X1 段?
            GetData(vpm, "Local HMI", LW, 70, 1)          // 读 X1 段位图 LW-70
        end if                                            // 结束判断
        if x == 2 then                                    // 当前在 X2 段?
            GetData(vpm, "Local HMI", LW, 71, 1)          // 读 X2 段位图 LW-71
        end if                                            // 结束判断
        if x == 3 then                                    // 当前在 X3 段?
            GetData(vpm, "Local HMI", LW, 72, 1)          // 读 X3 段位图 LW-72
        end if                                            // 结束判断
        if x == 4 then                                    // 当前在 X4 段?
            GetData(vpm, "Local HMI", LW, 73, 1)          // 读 X4 段位图 LW-73
        end if                                            // 结束判断
        GETBIT(vpm, bv, 1)                                // 取位 1(Y2)→ bv
        if bv == 1 then                                   // 该页面有内容?
            SetData(y, "Local HMI", LW, 1, 1)             // Y=2 → LW-1
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-y3(Y3 配方 · 权限 A/B/C/D)

macro_command main()                                      // 宏入口:按下 Y3 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 0                                           // 运行时读回的当前 X(1~4)
    short y = 3                                           // 本按钮固定切到 Y=3
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // 当前 X 段页面存在位图
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    short bD = 0                                          // 类别 D 位(制丸)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bC, 2)                                    // 取 bit2 → bC(C 上料)
    GETBIT(cls, bD, 3)                                    // 取 bit3 → bD(D 制丸)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bC == 1 then                                       // 含类别 C?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bD == 1 then                                       // 含类别 D?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 查当前 X 段位图
        GetData(x, "Local HMI", LW, 0, 1)                 // 读当前 X → x
        if x == 1 then                                    // 当前在 X1 段?
            GetData(vpm, "Local HMI", LW, 70, 1)          // 读 X1 段位图 LW-70
        end if                                            // 结束判断
        if x == 2 then                                    // 当前在 X2 段?
            GetData(vpm, "Local HMI", LW, 71, 1)          // 读 X2 段位图 LW-71
        end if                                            // 结束判断
        if x == 3 then                                    // 当前在 X3 段?
            GetData(vpm, "Local HMI", LW, 72, 1)          // 读 X3 段位图 LW-72
        end if                                            // 结束判断
        if x == 4 then                                    // 当前在 X4 段?
            GetData(vpm, "Local HMI", LW, 73, 1)          // 读 X4 段位图 LW-73
        end if                                            // 结束判断
        GETBIT(vpm, bv, 2)                                // 取位 2(Y3)→ bv
        if bv == 1 then                                   // 该页面有内容?
            SetData(y, "Local HMI", LW, 1, 1)             // Y=3 → LW-1
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-y4(Y4 设备参数 · 权限仅 A/B)

macro_command main()                                      // 宏入口:按下 Y4 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 0                                           // 运行时读回的当前 X(1~4)
    short y = 4                                           // 本按钮固定切到 Y=4
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // 当前 X 段页面存在位图
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 查当前 X 段位图
        GetData(x, "Local HMI", LW, 0, 1)                 // 读当前 X → x
        if x == 1 then                                    // 当前在 X1 段?
            GetData(vpm, "Local HMI", LW, 70, 1)          // 读 X1 段位图 LW-70
        end if                                            // 结束判断
        if x == 2 then                                    // 当前在 X2 段?
            GetData(vpm, "Local HMI", LW, 71, 1)          // 读 X2 段位图 LW-71
        end if                                            // 结束判断
        if x == 3 then                                    // 当前在 X3 段?
            GetData(vpm, "Local HMI", LW, 72, 1)          // 读 X3 段位图 LW-72
        end if                                            // 结束判断
        if x == 4 then                                    // 当前在 X4 段?
            GetData(vpm, "Local HMI", LW, 73, 1)          // 读 X4 段位图 LW-73
        end if                                            // 结束判断
        GETBIT(vpm, bv, 3)                                // 取位 3(Y4)→ bv
        if bv == 1 then                                   // 该页面有内容?
            SetData(y, "Local HMI", LW, 1, 1)             // Y=4 → LW-1
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-y5(Y5 I/O 监控 · 权限仅 A/B)

macro_command main()                                      // 宏入口:按下 Y5 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 0                                           // 运行时读回的当前 X(1~4)
    short y = 5                                           // 本按钮固定切到 Y=5
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // 当前 X 段页面存在位图
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 查当前 X 段位图
        GetData(x, "Local HMI", LW, 0, 1)                 // 读当前 X → x
        if x == 1 then                                    // 当前在 X1 段?
            GetData(vpm, "Local HMI", LW, 70, 1)          // 读 X1 段位图 LW-70
        end if                                            // 结束判断
        if x == 2 then                                    // 当前在 X2 段?
            GetData(vpm, "Local HMI", LW, 71, 1)          // 读 X2 段位图 LW-71
        end if                                            // 结束判断
        if x == 3 then                                    // 当前在 X3 段?
            GetData(vpm, "Local HMI", LW, 72, 1)          // 读 X3 段位图 LW-72
        end if                                            // 结束判断
        if x == 4 then                                    // 当前在 X4 段?
            GetData(vpm, "Local HMI", LW, 73, 1)          // 读 X4 段位图 LW-73
        end if                                            // 结束判断
        GETBIT(vpm, bv, 4)                                // 取位 4(Y5)→ bv
        if bv == 1 then                                   // 该页面有内容?
            SetData(y, "Local HMI", LW, 1, 1)             // Y=5 → LW-1
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-y6(Y6 报警 · 权限 A/B/C/D)

macro_command main()                                      // 宏入口:按下 Y6 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 0                                           // 运行时读回的当前 X(1~4)
    short y = 6                                           // 本按钮固定切到 Y=6
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // 当前 X 段页面存在位图
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    short bD = 0                                          // 类别 D 位(制丸)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bC, 2)                                    // 取 bit2 → bC(C 上料)
    GETBIT(cls, bD, 3)                                    // 取 bit3 → bD(D 制丸)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bC == 1 then                                       // 含类别 C?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bD == 1 then                                       // 含类别 D?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 查当前 X 段位图
        GetData(x, "Local HMI", LW, 0, 1)                 // 读当前 X → x
        if x == 1 then                                    // 当前在 X1 段?
            GetData(vpm, "Local HMI", LW, 70, 1)          // 读 X1 段位图 LW-70
        end if                                            // 结束判断
        if x == 2 then                                    // 当前在 X2 段?
            GetData(vpm, "Local HMI", LW, 71, 1)          // 读 X2 段位图 LW-71
        end if                                            // 结束判断
        if x == 3 then                                    // 当前在 X3 段?
            GetData(vpm, "Local HMI", LW, 72, 1)          // 读 X3 段位图 LW-72
        end if                                            // 结束判断
        if x == 4 then                                    // 当前在 X4 段?
            GetData(vpm, "Local HMI", LW, 73, 1)          // 读 X4 段位图 LW-73
        end if                                            // 结束判断
        GETBIT(vpm, bv, 5)                                // 取位 5(Y6)→ bv
        if bv == 1 then                                   // 该页面有内容?
            SetData(y, "Local HMI", LW, 1, 1)             // Y=6 → LW-1
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-y7(Y7 数据趋势 · 权限 A/B/C/D)

macro_command main()                                      // 宏入口:按下 Y7 按钮时执行
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 0                                           // 运行时读回的当前 X(1~4)
    short y = 7                                           // 本按钮固定切到 Y=7
    short win = 0                                         // 计算出的目标窗口号
    short ok = 0                                          // 权限通过标志:1=放行
    short bv = 0                                          // 页面存在标志:1=有内容
    short vpm = 0                                         // 当前 X 段页面存在位图
    short pop = 1                                         // 弹窗置位常量 1
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    short bD = 0                                          // 类别 D 位(制丸)
    GetData(cls, "Local HMI", LW, 9222, 1)                // 读当前用户类别位 → cls
    GETBIT(cls, bA, 0)                                    // 取 bit0 → bA(A 工程师)
    GETBIT(cls, bB, 1)                                    // 取 bit1 → bB(B 管理员)
    GETBIT(cls, bC, 2)                                    // 取 bit2 → bC(C 上料)
    GETBIT(cls, bD, 3)                                    // 取 bit3 → bD(D 制丸)
    if bA == 1 then                                       // 含类别 A?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bB == 1 then                                       // 含类别 B?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bC == 1 then                                       // 含类别 C?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if bD == 1 then                                       // 含类别 D?
        ok = 1                                            // 权限通过
    end if                                                // 结束判断
    if ok == 1 then                                       // 权限通过 → 查当前 X 段位图
        GetData(x, "Local HMI", LW, 0, 1)                 // 读当前 X → x
        if x == 1 then                                    // 当前在 X1 段?
            GetData(vpm, "Local HMI", LW, 70, 1)          // 读 X1 段位图 LW-70
        end if                                            // 结束判断
        if x == 2 then                                    // 当前在 X2 段?
            GetData(vpm, "Local HMI", LW, 71, 1)          // 读 X2 段位图 LW-71
        end if                                            // 结束判断
        if x == 3 then                                    // 当前在 X3 段?
            GetData(vpm, "Local HMI", LW, 72, 1)          // 读 X3 段位图 LW-72
        end if                                            // 结束判断
        if x == 4 then                                    // 当前在 X4 段?
            GetData(vpm, "Local HMI", LW, 73, 1)          // 读 X4 段位图 LW-73
        end if                                            // 结束判断
        GETBIT(vpm, bv, 6)                                // 取位 6(Y7)→ bv
        if bv == 1 then                                   // 该页面有内容?
            SetData(y, "Local HMI", LW, 1, 1)             // Y=7 → LW-1
            win = 101 + (x - 1) * 7 + (y - 1)             // 算窗口号:101+(X-1)*7+(Y-1)
            SetData(win, "Local HMI", LW, 2, 1)           // 窗口号 → LW-2(触发切窗)
        else                                              // 该页面无内容
            SetData(pop, "Local HMI", LB, 124, 1)         // 置 LB-124 → 弹 16 号窗
        end if                                            // 结束判断
    else                                                  // 权限不通过
        SetData(pop, "Local HMI", LB, 110, 1)             // 置 LB-110 → 弹 11 号无权限窗
    end if                                                // 结束判断
end macro_command                                         // 宏结束

btn-login-index(登录:索引登入 → 按组定位;结果提示已内联,无宏编号依赖)

macro_command main()                                      // 宏入口:点登录按钮时执行
    short cmd = 2                                         // 命令=2:使用索引登入(读 LW-102 选中的账号)
    short res = 0                                         // 存放命令结果(读 LW-101)
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 1                                           // 登录后入口 X 初值 = 1
    short y = 1                                           // 登录后入口 Y = 1
    short win = 0                                         // 计算出的目标窗口号
    short idx = 7                                         // 提示编号(默认 7=登录失败)
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    short bD = 0                                          // 类别 D 位(制丸)
    short z = 0                                           // 常量 0(清命令/清旧结果用)
    SetData(z, "Local HMI", LW, 101, 1)                   // 先清旧结果 → 防止读到上一次的码
    SetData(z, "Local HMI", LW, 261, 1)                   // 清底部提示 → 不显示上一次的信息
    SetData(z, "Local HMI", LW, 100, 1)                   // 命令先清 0(保证 0→2 有跳变)
    SetData(cmd, "Local HMI", LW, 100, 1)                 // 发命令 2 → LW-100(索引登入)
    DELAY(300)                                            // 等 0.3 秒
    GetData(res, "Local HMI", LW, 101, 1)                 // 读命令结果 → res(1=成功)
    if res == 0 then                                      // 结果仍为 0?
        DELAY(300)                                        // 再等 0.3 秒
        GetData(res, "Local HMI", LW, 101, 1)             // 再读一次
    end if                                                // 结束判断
    if res == 1 then                                      // 结果=1?
        idx = 1                                           // 提示编号 1:登录成功
    end if                                                // 结束判断
    if res == 8 then                                      // 结果=8?
        idx = 2                                           // 提示编号 2:账号不存在
    end if                                                // 结束判断
    if res == 16 then                                     // 结果=16?
        idx = 3                                           // 提示编号 3:密码错误
    end if                                                // 结束判断
    if res == 32 then                                     // 结果=32?
        idx = 4                                           // 提示编号 4:命令无法执行
    end if                                                // 结束判断
    if res == 64 then                                     // 结果=64?
        idx = 5                                           // 提示编号 5:名称不合法
    end if                                                // 结束判断
    if res == 128 then                                    // 结果=128?
        idx = 6                                           // 提示编号 6:密码含无效字符
    end if                                                // 结束判断
    if res == 0 then                                      // 结果=0?
        idx = 0                                           // 提示编号 0:不显示
    end if                                                // 结束判断
    SetData(idx, "Local HMI", LW, 261, 1)                 // 提示编号 → LW-261(立即显示)
    if res == 1 then                                      // 登录成功?
        GetData(cls, "Local HMI", LW, 9222, 1)            // 读当前用户类别位 → cls
        GETBIT(cls, bA, 0)                                // 取 bit0 → bA(A 工程师)
        GETBIT(cls, bB, 1)                                // 取 bit1 → bB(B 管理员)
        GETBIT(cls, bC, 2)                                // 取 bit2 → bC(C 上料)
        GETBIT(cls, bD, 3)                                // 取 bit3 → bD(D 制丸)
        if bD == 1 then                                   // 含类别 D(制丸)?
            x = 3                                         // 只有制丸组 → 入口 X3
        end if                                            // 结束判断
        if bA == 1 then                                   // 含类别 A?
            x = 1                                         // 入口 X1
        end if                                            // 结束判断
        if bB == 1 then                                   // 含类别 B?
            x = 1                                         // 入口 X1
        end if                                            // 结束判断
        if bC == 1 then                                   // 含类别 C?
            x = 1                                         // 入口 X1
        end if                                            // 结束判断
        win = 101 + (x - 1) * 7 + (y - 1)                 // 算目标窗口号:101+(X-1)*7+(Y-1)
        SetData(x, "Local HMI", LW, 0, 1)                 // X → LW-0
        SetData(y, "Local HMI", LW, 1, 1)                 // Y → LW-1
        SetData(win, "Local HMI", LW, 2, 1)               // 窗口号 → LW-2(触发切窗)
    end if                                                // 结束判断
end macro_command                                         // 宏结束

logout(注销并回登录页;10 号窗「打开窗口时执行」也用它)

macro_command main()                                      // 宏入口:点注销按钮 / 进 10 号窗时执行
    short cmd = 3                                         // 命令=3:注销当前账号
    short win = 10                                        // 目标窗口号 = 10(登录页)
    short z = 0                                           // 常量 0
    short z8[8] = {0,0,0,0,0,0,0,0}                       // 8 个 0(清用户名/密码框用)
    SetData(cmd, "Local HMI", LW, 100, 1)                 // 发命令 3 → LW-100(注销)
    SetData(z, "Local HMI", LW, 0, 1)                     // X 清 0
    SetData(z, "Local HMI", LW, 1, 1)                     // Y 清 0
    SetData(z8[0], "Local HMI", LW, 104, 8)               // 清用户名框 LW-104~111(下次默认下拉登入)
    SetData(z8[0], "Local HMI", LW, 112, 8)               // 清密码框 LW-112~119
    SetData(win, "Local HMI", LW, 2, 1)                   // 窗口号 10 → LW-2(切回登录页)
end macro_command                                         // 宏结束

admin-open(登录页 logo 上透明按钮,长按 3 秒)

macro_command main()                                      // 宏入口:长按登录页隐藏按钮时执行
    short on = 1                                          // 置位常量 1
    short z8[8] = {0,0,0,0,0,0,0,0}                       // 8 个 0(先清输入框)
    SetData(z8[0], "Local HMI", LW, 104, 8)               // 清用户名框 LW-104~111
    SetData(z8[0], "Local HMI", LW, 112, 8)               // 清密码框 LW-112~119
    SetData(on, "Local HMI", LB, 125, 1)                  // 置 LB-125 → 弹 17 号管理员登录窗
end macro_command                                         // 宏结束

admin-login(17 号窗「登录」:固定名称登入)

macro_command main()                                      // 宏入口:点 17 号窗「登录」时执行
    short cmd = 1                                         // 命令=1:使用名称登入(读 LW-104 + LW-112)
    short res = 0                                         // 命令结果(读 LW-101)
    short cls = 0                                         // 当前用户类别位(读 LW-9222)
    short x = 1                                           // 入口 X 初值 = 1
    short y = 1                                           // 入口 Y = 1
    short win = 0                                         // 计算出的目标窗口号
    short idx = 7                                         // 提示编号(默认 7=登录失败)
    short bA = 0                                          // 类别 A 位(工程师)
    short bB = 0                                          // 类别 B 位(管理员)
    short bC = 0                                          // 类别 C 位(上料)
    short bD = 0                                          // 类别 D 位(制丸)
    short z = 0                                           // 常量 0
    SetData(z, "Local HMI", LW, 101, 1)                   // 先清旧结果 → 防止读到上一次的码
    SetData(z, "Local HMI", LW, 261, 1)                   // 清旧提示
    SetData(z, "Local HMI", LW, 100, 1)                   // 命令先清 0
    SetData(cmd, "Local HMI", LW, 100, 1)                 // 发命令 1 → LW-100(名称登入)
    DELAY(300)                                            // 等 0.3 秒
    GetData(res, "Local HMI", LW, 101, 1)                 // 读命令结果 → res(1=成功)
    if res == 0 then                                      // 结果仍为 0?
        DELAY(300)                                        // 再等 0.3 秒
        GetData(res, "Local HMI", LW, 101, 1)             // 再读一次
    end if                                                // 结束判断
    if res == 1 then                                      // 结果=1?
        idx = 1                                           // 提示编号 1:登录成功
    end if                                                // 结束判断
    if res == 8 then                                      // 结果=8?
        idx = 2                                           // 提示编号 2:账号不存在
    end if                                                // 结束判断
    if res == 16 then                                     // 结果=16?
        idx = 3                                           // 提示编号 3:密码错误
    end if                                                // 结束判断
    if res == 32 then                                     // 结果=32?
        idx = 4                                           // 提示编号 4:命令无法执行
    end if                                                // 结束判断
    if res == 64 then                                     // 结果=64?
        idx = 5                                           // 提示编号 5:名称不合法
    end if                                                // 结束判断
    if res == 128 then                                    // 结果=128?
        idx = 6                                           // 提示编号 6:密码含无效字符
    end if                                                // 结束判断
    if res == 0 then                                      // 结果=0?
        idx = 0                                           // 提示编号 0:不显示
    end if                                                // 结束判断
    SetData(idx, "Local HMI", LW, 261, 1)                 // 提示编号 → LW-261(立即显示)
    if res == 1 then                                      // 登录成功?
        SetData(z, "Local HMI", LB, 125, 1)               // 清 LB-125 → 关 17 号弹窗
        GetData(cls, "Local HMI", LW, 9222, 1)            // 读当前用户类别位 → cls
        GETBIT(cls, bA, 0)                                // 取 bit0 → bA
        GETBIT(cls, bB, 1)                                // 取 bit1 → bB
        GETBIT(cls, bC, 2)                                // 取 bit2 → bC
        GETBIT(cls, bD, 3)                                // 取 bit3 → bD
        if bD == 1 then                                   // 含类别 D?
            x = 3                                         // 只有制丸组 → X3
        end if                                            // 结束判断
        if bA == 1 then                                   // 含类别 A?
            x = 1                                         // 入口 X1
        end if                                            // 结束判断
        if bB == 1 then                                   // 含类别 B?
            x = 1                                         // 入口 X1
        end if                                            // 结束判断
        if bC == 1 then                                   // 含类别 C?
            x = 1                                         // 入口 X1
        end if                                            // 结束判断
        win = 101 + (x - 1) * 7 + (y - 1)                 // 算目标窗口号
        SetData(x, "Local HMI", LW, 0, 1)                 // X → LW-0
        SetData(y, "Local HMI", LW, 1, 1)                 // Y → LW-1
        SetData(win, "Local HMI", LW, 2, 1)               // 窗口号 → LW-2(触发切窗)
    end if                                                // 结束判断
end macro_command                                         // 宏结束

admin-close(17 号窗「取消」)

macro_command main()                                      // 宏入口:点 17 号窗「取消」时执行
    short z = 0                                           // 常量 0
    short z8[8] = {0,0,0,0,0,0,0,0}                       // 8 个 0
    SetData(z, "Local HMI", LB, 125, 1)                   // 清 LB-125 → 关 17 号窗
    SetData(z8[0], "Local HMI", LW, 104, 8)               // 清用户名框
    SetData(z8[0], "Local HMI", LW, 112, 8)               // 清密码框
end macro_command                                         // 宏结束

close-msg(关闭无权限提示窗)

macro_command main()                                      // 宏入口:点 11 号窗「确定」时执行
    short z = 0                                           // 常量 0
    SetData(z, "Local HMI", LB, 110, 1)                   // 清 LB-110 → 关闭 11 号无权限窗
end macro_command                                         // 宏结束

close-nopage(关闭 16 号「无内容」提示窗)

macro_command main()                                      // 宏入口:点 16 号窗「确定」时执行
    short z = 0                                           // 常量 0
    SetData(z, "Local HMI", LB, 124, 1)                   // 清 LB-124 → 关闭 16 号无内容窗
end macro_command                                         // 宏结束

用户管理 usr-*

宏 命令 说明
usr-init — 14 号窗「打开窗口时执行」:清 LW-104/LW-112/LW-103/LW-101,清 LB-121
user-open — 顶部按钮:仅 B/admin → 置 LB-122
usr-back — 清 LB-122
usr-add 5 新增(LW-104+LW-112+LW-103)
usr-delete — 置 LB-121 弹确认
usr-delete-ok 8 按索引删除
usr-delete-cancel — 清 LB-121
usr-getlevel 14 按索引读权限 → LW-103
usr-setlevel 10 按索引设权限
usr-setpwd 12 按索引设密码
macro_command main()                                      // 宏入口:用户管理按钮(以 cmd=14 读取权限为例)
    short cmd = 14                                        // 命令值:改成 5/8/10/12 等即为对应功能
    SetData(cmd, "Local HMI", LW, 100, 1)                 // 发命令 → LW-100
end macro_command                                         // 宏结束

其余 usr-* 同上模板:cmd = 5 / 8 / 10 / 12;user-open 置 / usr-back 清 LB-122,usr-delete / usr-delete-cancel 置 / 清 LB-121,usr-init 清输入。

关于窗 about-*:about-open 置 LB-123(弹 13 号窗);about-close 清 LB-123。

修改时间

宏 触发 作用
open-time 时钟上透明按钮(仅 B) 读 LW-90179022 → LW-4045,置 LB-120
set-time 12 号窗「设定」 LW-4045 → LW-90179022(设置 RTC),清 LB-120
close-time 12 号窗「取消」 清 LB-120
macro_command main()                                      // 宏入口:改时间弹窗点「设定」时执行
    short v[6] = {0,0,0,0,0,0}                            // 暂存数组:秒/分/时/日/月/年
    short z = 0                                           // 常量 0
    GetData(v[0], "Local HMI", LW, 40, 6)                 // 从 LW-40~45 读入 6 个值 → v
    SetData(v[0], "Local HMI", LW, 9017, 6)               // v 写入 RTC 寄存器 LW-9017~9022(设置时间)
    SetData(z, "Local HMI", LB, 120, 1)                   // 清 LB-120 → 关闭 12 号弹窗
end macro_command                                         // 宏结束

网络设置 net-*

net-read(13 号窗「打开窗口时执行」:系统值 → 暂存 RW)

macro_command main()                                      // 宏入口:进 13 号窗时执行
    short v[4] = {0,0,0,0}                                // 4 字暂存数组
    short line = 1                                        // 制丸线别(1=A / 2=B)
    GetData(line, "Local HMI", RW, 61, 1)                 // 读 RW-61 线别
    GetData(v[0], "Local HMI", LW, 9129, 4)               // 读 HMI IP
    SetData(v[0], "Local HMI", RW, 20, 4)                 // → RW-20~23
    GetData(v[0], "Local HMI", LW, 10750, 4)              // 读 HMI 掩码
    SetData(v[0], "Local HMI", RW, 24, 4)                 // → RW-24~27
    GetData(v[0], "Local HMI", LW, 9125, 4)               // 读 HMI 网关
    SetData(v[0], "Local HMI", RW, 28, 4)                 // → RW-28~31
    GetData(v[0], "Local HMI", LW, 9600, 4)               // 读上料 PLC(设备4)IP
    SetData(v[0], "Local HMI", RW, 40, 4)                 // → RW-40~43
    GetData(v[0], "Local HMI", LW, 9605, 4)               // 读制丸 PLC(设备5)IP
    if line == 1 then                                     // 当前选 A 线?
        SetData(v[0], "Local HMI", RW, 44, 4)             // → RW-44~47(制丸A)
    else                                                  // 当前选 B 线
        SetData(v[0], "Local HMI", RW, 48, 4)             // → RW-48~51(制丸B)
    end if                                                // 结束判断
end macro_command                                         // 宏结束

net-apply(13 号窗「应用」按钮;另勾选宏属性「当 HMI 启动时即执行一次」,开机自动套用)

macro_command main()                                      // 宏入口:点「应用」/ 开机自动套用
    short v[4] = {0,0,0,0}                                // 4 字暂存数组
    short hmi0 = 0                                        // HMI IP 第一段(判断是否配置过)
    short role = 0                                        // 本机角色(1上料/2制丸A/3制丸B)
    short line = 1                                        // 制丸线别(1=A/2=B)
    short okp = 0                                         // 是否连接制丸设备:1=连
    short on = 1                                          // 置位常量
    short z = 0                                           // 常量 0
    GetData(hmi0, "Local HMI", RW, 20, 1)                 // 读 RW-20(HMI IP 第一段)
    GetData(role, "Local HMI", RW, 60, 1)                 // 读 RW-60(本机角色)
    GetData(line, "Local HMI", RW, 61, 1)                 // 读 RW-61(线别)
    if line == 0 then                                     // 线别未设置/无效?
        line = 1                                          // 默认 A 线
    end if                                                // 结束判断
    if role == 1 then                                     // 上料屏?
        okp = 1                                           // 连制丸设备(线别按 RW-61)
    end if                                                // 结束判断
    if role == 2 then                                     // 制丸A屏?
        line = 1                                          // 强制 A 线
        okp = 1                                           // 连制丸设备
    end if                                                // 结束判断
    if role == 3 then                                     // 制丸B屏?
        line = 2                                          // 强制 B 线
        okp = 1                                           // 连制丸设备
    end if                                                // 结束判断
    SetData(line, "Local HMI", RW, 61, 1)                 // 回写线别(界面显示一致)
    if hmi0 == 0 then                                     // HMI IP 没填 → 跳过 HMI 部分
        hmi0 = 0                                          // (保持原 HMI 网络)
    else                                                  // 已填 → 应用 HMI 网络
        GetData(v[0], "Local HMI", RW, 20, 4)             // RW → HMI IP
        SetData(v[0], "Local HMI", LW, 9129, 4)           // 写 LW-9129~9132
        GetData(v[0], "Local HMI", RW, 24, 4)             // RW → HMI 掩码
        SetData(v[0], "Local HMI", LW, 10750, 4)          // 写 LW-10750~10753
        GetData(v[0], "Local HMI", RW, 28, 4)             // RW → HMI 网关
        SetData(v[0], "Local HMI", LW, 9125, 4)           // 写 LW-9125~9128
        SetData(on, "Local HMI", LB, 12094, 1)            // 置 LB-12094 → HMI 网络生效
    end if                                                // 结束判断
    GetData(v[0], "Local HMI", RW, 40, 4)                 // RW → 上料 PLC IP
    SetData(v[0], "Local HMI", LW, 9600, 4)               // 写上料 PLC(设备4)
    SetData(on, "Local HMI", LB, 10070, 1)                // 重连设备4
    if okp == 1 then                                      // 需要连制丸设备?
        if line == 1 then                                 // 选 A 线
            GetData(v[0], "Local HMI", RW, 44, 4)         // RW → 制丸A IP
        else                                              // 选 B 线
            GetData(v[0], "Local HMI", RW, 48, 4)         // RW → 制丸B IP
        end if                                            // 结束判断
        SetData(v[0], "Local HMI", LW, 9605, 4)           // 写制丸 PLC(设备5)
        SetData(on, "Local HMI", LB, 10071, 1)            // 重连设备5
    end if                                                // 结束判断(角色=0:不连制丸设备,防误连)
    DELAY(1000)                                           // 等 1 秒
    SetData(z, "Local HMI", LB, 12094, 1)                 // 清 LB-12094
    SetData(z, "Local HMI", LB, 10070, 1)                 // 清 LB-10070
    SetData(z, "Local HMI", LB, 10071, 1)                 // 清 LB-10071
end macro_command                                         // 宏结束

line-ab-switch(13 号窗「A/B 切换」按钮;仅本机角色=1 生效)

macro_command main()                                      // 宏入口:点 A/B 线切换按钮
    short v[4] = {0,0,0,0}                                // 4 字暂存数组
    short role = 0                                        // 本机角色(读 RW-60)
    short line = 1                                        // 当前线别
    short on = 1                                          // 置位常量
    short z = 0                                           // 常量 0
    GetData(role, "Local HMI", RW, 60, 1)                 // 读本机角色
    if role == 1 then                                     // 只有上料屏允许切换
        GetData(line, "Local HMI", RW, 61, 1)             // 读当前线别
        if line == 1 then                                 // 当前 A → 切 B
            line = 2                                      // 目标 B
        else                                              // 当前 B → 切 A
            line = 1                                      // 目标 A
        end if                                            // 结束判断
        SetData(line, "Local HMI", RW, 61, 1)             // 写回 RW-61
        if line == 1 then                                 // 目标 A 线
            GetData(v[0], "Local HMI", RW, 44, 4)         // 取制丸A IP
        else                                              // 目标 B 线
            GetData(v[0], "Local HMI", RW, 48, 4)         // 取制丸B IP
        end if                                            // 结束判断
        SetData(v[0], "Local HMI", LW, 9605, 4)           // 写设备5 IP
        SetData(on, "Local HMI", LB, 10071, 1)            // 重连设备5
        DELAY(1000)                                       // 等 1 秒
        SetData(z, "Local HMI", LB, 10071, 1)             // 清重连位
    end if                                                // 非上料屏:无动作(线别锁定)
end macro_command                                         // 宏结束

7. 各页面要点

7.1 登录页(10 号窗)

  • 项目选单(来源=用户账号)→ 写 LW-102;密码字符输入(勾系统键盘)→ LW-112。
  • admin 登录(隐藏入口):登录页 logo 上盖透明功能键(安全页「最少按键时间」= 3 秒)→ admin-open;10 号窗放「直接窗口」LB-125→17,弹窗里填 admin + 密码 → admin-login(名称登入),取消 → admin-close。
  • 底部提示用文字元件 + 字符串表,编号 LW-261(由登录宏直接写入)。
  • 「登录」按钮 → btn-login-index。
  • 窗口属性 → 「打开窗口时执行」= logout(每次回来强制重新登录)。
  • 顶部账户显示读 PLW-10754,上面盖透明功能键 → 长按 1s(安全页最少按键时间=1)→ logout。

7.2 公共窗口(100 号窗)

  • 顶部:品牌、页签、?/首页/用户/报警图标;右上角「日期/时间」元件。
  • PLC 联机:触发条件(3 台联机位 AND)→ LB-131 → 位状态指示灯。
  • 直接窗口 ×6:LB-110→11、LB-120→12、LB-121→15、LB-122→14、LB-123→13、LB-124→16。
  • 入口按钮:点检(功能键→切换基本窗口=20);关于(仅 A,安全页操作类别=类别 A);用户管理(user-open)。
  • X/Y 导航按钮:btn-x1~x4、btn-y1~y7。

7.3 用户管理(14 号窗,1200×800 弹窗)

见第 6 节 usr-*:列表(LW-102)+ 用户名/密码 + 4 个权限开关(LW_Bit-10300~10303)+ 读取权限/修改权限/修改密码/删除/新增/关闭 + 结果 LW-101。 只读管理员(B/admin)可用;改权限前先「读取权限」。

7.4 点检(20 号窗)

  • 只做了画面(可退出),暂无功能、暂不设计;不放宏。
  • 「关闭」等功能键直接切窗口(未用宏)。

7.5 关于 / 设置(13 号窗,1200×800 弹窗)

  • 上方:关于信息,字段与地址见 5.7;LB-123 弹窗;入口仅工程师(A)。
  • 下方「设置」区(全部存 RW,掉电保持):
    • 本机角色 → RW-60(1 上料屏 / 2 制丸A屏 / 3 制丸B屏);
    • 制丸线选择 → RW-61(1 A 线 / 2 B 线,上料屏用);
    • HMI IP / 掩码 / 网关 → RW-20~31(备屏/换屏用;HMI 固定 IP 必填);
    • 上料 PLC IP → RW-4043;制丸A PLC IP → RW-4447;制丸B PLC IP → RW-48~51;
    • 「应用」→ net-apply;窗口开启时执行 net-read(进窗自动带出当前值,不需要单独做「读取当前」按钮)。
  • 上料屏在 13 号窗设置区放「A/B 切换」按钮 → line-ab-switch(只切 设备5 的 IP 并重连;仅本机角色=1 生效,A/B 屏误点无效);旁边放当前线别显示(读 RW-61)。
  • 现场无需 EB:换屏/换 PLC 只填这一页,点「应用」即可。

7.6 修改时间(12 号窗)

  • 6 个数值输入(LW-40~45 秒/分/时/日/月/年)+ 设定/取消。
  • 入口:100 号窗时钟上透明功能键(仅 B)→ open-time。

7.7 主画面(101~128)设计

每个画面 = 某工艺段 × 某功能组合,底图按 XD/pic 素材放置;其余页面待逐个补。

7.7.1 X1Y1(配料/上料 · 初始状态)完整操作表

地址栏留空(__)处待 PLC 工程师填写;元件先建好、地址后补。

布局(1920×1080;内容区 x 1101900,y 901000)

# 区域 控件 元件 地址 位置(x,y) 尺寸
1 批次行 「产品配方」标签 文字 — 190,110 —
2 批次行 产品配方 数值输入 LW-__ 250,105 210×44
3 批次行 选择 功能键 / 位状态设置 LB-__ 470,105 90×46
4 批次行 「料次」标签 文字 — 580,110 —
5 批次行 料次 数值输入 LW-__ 640,105 100×44
6 批次行 「批号」标签 文字 — 760,110 —
7 批次行 批号(存储) 字符输入(8 words) RW-__ 820,105 220×44
8 批次行 「报警信息」标签 文字 — 1060,110 —
9 批次行 报警信息 报警条(一行滚动) 事件登录 1140,105 500×44
10 批次行 变频器复位 功能键 / 位状态设置 LB-__ 1700,105 180×46
11 流程1组 节点1~6 多状态指示灯 LW-__ ×6 x=680/850/1020/1190/1360/1530,y=300 160×56
12 流程1组 节点1~6 修正 数值输入(仅工程师) 同节点地址 同 x,y=362 100×40
13 状态区 圆形开关 位状态指示灯 LB-__ 700,430 80×50
14 状态区 进度条 棒图(0~100) LW-__ 250,470 400×20
15 状态区 「投料 / 输送1」标签 文字 — 250,435 —
16 流程2组 节点7~12 多状态指示灯 LW-__ ×6 同 x,y=560 160×56
17 流程2组 节点7~12 修正 数值输入(仅工程师) 同节点地址 同 x,y=622 100×40
18 状态区 圆形开关 位状态指示灯 LB-__ 700,690 80×50
19 状态区 进度条 棒图(0~100) LW-__ 250,730 400×20

节点状态配色(多状态指示灯「状态设置」)

值 含义 颜色/图片
0 未开始 灰
1 进行中 绿
2 已完成 蓝
3 异常 红

操作步骤(从上到下逐个做)

0. 准备

  1. 打开 101 号窗口(X1Y1)。
  2. 工程文件 → 地址(地址标签库)建标签(地址先用占位,PLC 给了再换): 产品配方 LW-330、料次 LW-331、批号 RW-0、节点状态 LW-300~311、进度 LW-320/321、圆形开关 LB-300/301、选择 LB-311、变频器复位 LB-310。

① 批次信息行(y≈105) 3. 元件 → 绘图 → 文字/批注 放 4 个标签:「产品配方」(190,110)、「料次」(580,110)、「批号」(760,110)、「报警信息」(1060,110)。 4. 产品配方:元件 → 输入 → 数值,地址 LW-330,(250,105) 210×44;属性「数值输入」页勾 系统键盘。 5. 料次:数值输入,地址 LW-331,(640,105) 100×44,勾系统键盘。 6. 批号:元件 → 输入 → 字符,地址 RW-0、字长 8,(820,105) 220×44,勾系统键盘(RW 掉电保持 → 自动保存)。 7. 「选择」:元件 → 按钮/开关 → 功能键,动作选 位状态设置 或 宏指令,写 LB-311,(470,105) 90×46;「图片」标签可设按钮底图。 8. 「变频器复位」:功能键,写 LB-310,(1700,105) 180×46。 9. 报警信息:元件 → 报警 → 报警条,(1140,105) 500×44(报警来源见 ⑥)。

② 流程节点(两组 × 6) 10. 元件 → 指示灯 → 多状态指示灯,放 6 个:x = 680 / 850 / 1020 / 1190 / 1360 / 1530,y = 300,160×56;地址 LW-300305。 11. 「状态设置」给 4 个状态配图/色:0 灰(未开始)、1 绿(进行中)、2 蓝(完成)、3 红(异常)。 12. 复制这 6 个到第二组 y = 560,地址改 LW-306311。

③ 节点人工修正(显示+可改都用同一地址) 13. 元件 → 输入 → 数值,放在每个节点下方:同 x、y = 362、100×40;地址=对应节点状态字(LW-300305)。 14. 属性 「安全」页 → 操作类别 = 类别 A(工程师)(只有工程师能改);可勾 「操作前先确认」 防误改。 15. 第二组同样:y = 622,地址 LW-306311。

④ 圆形开关 16. 元件 → 指示灯 → 位状态指示灯,(700,430) 80×50,地址 LB-300;状态 0/1 各配一张圆图(灰/绿)。 17. 第二组 (700,690),地址 LB-301。

⑤ 进度条 18. 元件 → 图例 → 棒图,(250,470) 400×20,地址 LW-320,方向横向、范围 0~100、绿色。 19. 上方加文字「投料 / 输送1」(250,435)。 20. 第二组 (250,730),地址 LW-321。

⑥ 报警 21. 工程文件 → 事件/报警 新建报警条目(地址、触发条件、显示文字、颜色)。 22. 本页用 报警条(步骤 9)做滚动提示;完整列表与确认放在 Y6 报警表页(见 7.7.2)。

⑦ 收尾 23. 各元件「安全」页按需限制操作类别(只显示/只工程师可改)。 24. 保存编译;等 PLC 地址到位后,把占位地址逐个替换即可,元件与布局不动。 底部 X 页签:由 100 号公共窗口的 4 个功能键实现(配料/上料、上料清洗、制丸、制丸清洗 → 切换基本窗口)。

7.7.2 Y6 报警表页面(106 / 113 / 120 / 127)

Y6 = 报警功能;各工艺段各有一个报警表页:106(配料/上料)、113(上料清洗)、120(制丸)、127(制丸清洗)。

控件 元件 关键设置
报警列表 报警显示(或事件显示) 显示全部/未确认报警,一行一条,可上下滚动/翻页
顶部滚动条 报警条 一行滚动显示最新报警(同 X1Y1 的报警行)
报警确认 报警显示元件属性 → 「确认地址」 = LW-__ 操作员确认时,系统把该报警的确认值写入此地址(PLC 可读,用于联动/记录)
清除(当前记录) 位状态设置 → LB-9021(OFF→ON 重置当前事件记录) 系统位
删除事件记录文件 位状态设置 → LB-9023(删除 HMI 内存全部事件记录) 系统位
翻页 / 确认按钮 报警显示元件自带按钮,或自定义功能键 —

做法

  1. 工程文件 → 事件/报警 里逐条建立报警(地址、触发条件、文字、颜色、类别)。
  2. 在 Y6 页放 报警显示 元件(多行列表);X1Y1 等页放 报警条 元件(一行滚动)。
  3. 报警显示的属性里设 确认地址,并为每条报警配确认值 → 操作员确认后写回该地址给 PLC。
  4. 需要"清除"按钮就放 位状态设置 写 LB-9021(重置)/LB-9023(删除文件)。
  5. 4 个 Y6 页可共用同一套事件登录,也可按类别过滤(配料/上料、上料清洗、制丸、制丸清洗各自的报警)。

7.8 弹窗规范(统一用直接窗口)

  • 所有弹窗(11/12/13/14/15)都用 直接窗口元件,放在 100 号公共窗口,由触发位置位弹出。
  • 关键:直接窗口元件属性里的 「重叠」必须选「独立窗口」(cMT/cMT X),否则子窗口画在公共窗口那一层,会被主画面元素盖住;选「独立窗口」后显示在当前画面之上。
  • 弹窗内容做在各自窗口里;关闭用宏清位(如 close-msg 清 LB-110)。
弹窗 触发位 窗口号
无权限提示 LB-110 11
修改时间 LB-120 12
关于 LB-123 13
用户管理 LB-122 14
删除确认 LB-121 15
无内容提示 LB-124 16
管理员登录 LB-125 17

8. 素材清单(pic/)

素材 说明
common-bg.png / bottom-y.png 早期背景与按钮底板
white-bg.png 1920×1080 纯白底
transparent.png 240×48 全透明(透明功能键底图)
start-bg.png / start-equip.png 启动屏厂区航拍 / 设备渲染
pid-illustration.png / btn-back.png / btn-next.png / pid-logo.png 点检页素材(取自 XD B_PID)
lwaddr.csv 系统地址大全

9. 多线共屏:完整方法与流程

9.1 分工与连接

屏幕 角色 RW-60 连哪两台 PLC 能看到
上料屏 1 上料(设备4)+ 制丸 A 或 B(设备5,可切) 上料段 + 当前选的制丸线
制丸A屏 2 上料(设备4)+ 制丸A(设备5) 上料段 + A 线
制丸B屏 3 上料(设备4)+ 制丸B(设备5) 上料段 + B 线
  • 用户组:C → X1/X2(绑设备4);D → X3/X4(绑设备5);admin 全开;
  • 制丸A/B 互不可见:各屏只连自己那条线 + 上料;唯一例外是上料屏(D 登录可切 A/B)。

9.2 EB Pro 一次性改造

  1. 设备列表:确认以太网 PLC 占设备4/设备5;X1/X2 与 C 组数据绑设备4,X3/X4 与 D 组数据绑设备5。
  2. 13 号窗设置区(RW 全部掉电保持):
    • 本机角色 RW-60(1 上料 / 2 制丸A / 3 制丸B)、线别 RW-61(1=A / 2=B);
    • HMI IP/掩码/网关 RW-20~31(备屏/换屏用;HMI 固定 IP 必填);
    • 上料 IP RW-40~43、制丸A IP RW-44~47、制丸B IP RW-48~51;
    • 「应用」= net-apply(勾「当 HMI 启动时即执行一次」);进窗自动执行 net-read。
  3. 宏:第 6 节清单(main-init 写 LW-70~73 位图、btn-x/y、登录内联版、logout、user-open/usr-*、about-*、时间三支、close-msg/close-nopage、admin-*、net-read/net-apply/line-ab-switch)。
  4. 弹窗:11/12/13/14/15/16/17 的直接窗口都放 100 号公共窗,属性「重叠」= 独立窗口。
  5. 切换按钮暂放 13 号窗设置区:「A/B 切换」功能键 → line-ab-switch(仅 role=1 生效)+ 当前线别显示(读 RW-61);后续再移到制丸页给 D 用(通讯灯 LB-10400 一并加)。

9.3 装机流程(每台屏,刷同一工程)

步骤 上料屏 制丸A屏 制丸B屏
1 设 HMI 网络 IP(屏系统设置或 13 号窗) 同 同
2 13 号窗角色 = 1 = 2 = 3
3 填 上料 IP + 制丸A IP + 制丸B IP 填 上料 IP + 制丸A IP 填 上料 IP + 制丸B IP
4 线别选 A(或 B) 自动锁 A 自动锁 B
5 点「应用」 同 同
6 查 LB-10100(上料)、LB-10400(制丸)都 ON 同 同

9.4 现场运行

  • 上料屏切制丸线:暂在 13 号窗点「A/B 切换」→ 等 LB-10400 ON 再操作(默认 A、重启回 A);能开 13 号窗的是工程师 A / admin,后续移到制丸页后 D 组可用;
  • 制丸A/B 屏天然只看本线;admin 在制丸屏也切不动(line-ab-switch 角色校验);
  • C 组只看上料段;工程师 A 看「关于/设置」;管理员 B 用「用户管理/改时间」。

9.5 换件流程

场景 操作
HMI 屏坏 备件刷同工程 → 13 号窗设角色/线别/各组 IP → 应用
PLC 坏、IP 变 在对应屏 13 号窗改该线 IP(上料屏改 A 或 B 组)→ 应用
整线换 PLC(IP 不变) 不用动
屏挪到另一条线(A↔B 换屏) 13 号窗把角色 RW-60 改 2↔3,核对本线 IP;HMI 是固定 IP:一并把 HMI IP 改成新位置规划的 IP(系统设置或 13 号窗 RW-20~31)→ 应用

9.6 注意

  • PLC 在线改 IP 只支持本机以太网设备 4 及以后(1~3 是 COM);
  • 切换 A/B 后要等 LB-10400=ON 再操作(防旧数据);
  • A/B 屏不要给对方的线留 IP/加设备槽;要"上料屏同时看 A+B"才加设备6(另案);
  • 系统弹窗可用 LB-11963/LB-11964 禁掉。

10. 变更记录(里程碑)

日期 内容
09-23 解包分析现有工程;确定机型/分辨率/运行时与 XD 设计源
09-23 建立窗口骨架与命名规则(X1X4 × Y1Y7 → 101~128)
09-23 导航机制落地:宏算 LW-2 + PLC 控制元件切换基本窗口
09-23 完成登录页、权限控制(LW-9222)、按钮宏与无权限弹窗
09-24 完成用户管理(索引命令)、点检页、关于页、修改时间、PLC 联机状态、登录提示框
09-24 文档精简;准备开始 28 个主画面的设计
09-25 按钮宏全部改为逐行注释版(btn-x1~x4/btn-y1~y7,其余宏同步补注释);新增「该页面无内容」提示:LB-124→16 号窗 + close-nopage;LW-70~73 页面存在位图由 main-init 初始化
09-25 X 段更名:X2 上料清洗、X3 制丸、X4 制丸清洗(X1 配料/上料不变);权限调整:X2=A/B/C、X4=A/B/D(X1=A/B/C、X3=A/B/D,Y1~Y7 不变)
09-25 登录提示加速:btn-login-index 的 DELAY(2000) 改为「清旧结果 + 0.3s 读 + 0.3s 补读一次」,并在点登录时先清 LW-261 旧提示
09-25 Y6/Y7 开放给 C、D 用户组:权限改为 A/B/C/D(Y4/Y5 仍仅 A/B,Y1~Y3 不变)
09-25 新增「权限分配总表」(4.1):按用户组列出全部已开发功能的权限
09-25 权限表改为三级:● 拥有权限 / ○ 仅可见 / × 不可见,并补充 EB Pro 对应设置方法(操作类别、无权限时隐藏)
09-25 修正登录/点检入口:制丸 D 登录后落到 X3Y1(段名调整后遗漏,原为 X2Y1)
09-25 文档整理:删除未使用内容(insp-next、LW-260、cxob 资产条目);合并重复的 7.7 标题;修正 7.3/7.4 失效引用
09-25 登录页加用户名输入框 LW-104:填名称(admin)按名称登入,留空按下拉索引登入;logout 回登录页时清空用户名/密码框
09-25 admin 登录改为隐藏入口:登录页 logo 长按 3 秒 → 弹 17 号窗名称登入(LB-125 + admin-open/admin-login/admin-close);撤销可见用户名框,btn-login-index 恢复纯索引登入
09-25 新增 5.8 通用程序 IP 在线设置:HMI IP/掩码/网关(LW-9129 等 + LB-12094)、PLC IP(设备 4+:LW-9600 等 + LB-1007x)及现场设置页做法
09-25 网络设置落在 13 号窗(关于页):RW-20~35 暂存,新增 net-read/net-apply 宏,7.5/5.8 同步
09-25 窗口名按实际工程同步(10 登陆和入口、11 弹出、13 系统IP和关于、15 用户删除确认、16 空页面弹窗、17 超管登陆、20 点检check)
09-25 宏清单与工程实际对齐:user-open、usr-delete*、about-*;新增宏编号警告(msg-login=ID 031 被 btn-login-index 写死)
09-25 去掉高危数字宏号:msg-login 改由触发位 LB-126 驱动(执行条件 ON、结束自清);btn-login-index/admin-login 只置 LB-126;net-apply 用「HMI 启动时执行一次」
09-25 点检页只保留画面(可退出、暂不设计功能):删除 insp-init 宏及相关描述
09-25 确认保留 logout、废弃 log-out;net-read/net-apply 同步新 RW 规划(RW-40~51、RW-60 角色、RW-61 线别)并新增 line-ab-switch
09-25 5.8 补充切换互锁:设备通讯状态 LB-10100/LB-10400、错误码 LW-9403/9404、禁止系统弹窗 LB-11963/11964
09-25 登录提示改为内联(零延迟):映射写进 btn-login-index/admin-login,删除 msg-login 与 LB-126
09-25 多线共屏防串线:line-ab-switch 增加角色校验,仅上料屏(RW-60=1)可切 A/B;制丸A/B 屏线别由角色锁定
09-25 新增 5.9 拓扑图(3 PLC × 3 HMI × 2 设备槽的逻辑连接)
09-25 新增第 9 章「多线共屏完整方法与流程」:分工/一次性改造/装机/运行/换件/注意;变更记录顺延为第 10 章
09-25 上料屏制丸线默认 A:main-init 开机写 RW-61=1,net-apply 线别=0 按 A;只有人工切换才到 B
09-25 去掉多余的「读取当前」按钮:13 号窗打开时已自动执行 net-read
09-25 A/B 切换按钮暂放 13 号窗设置区(仅 role=1 生效);后续再移到制丸页给 D 用
09-25 制丸屏装机只改角色:A屏 role=2(锁A)、B屏 role=3(锁B);角色=0 时 net-apply 不连制丸设备,防装错位置误连
09-25 9.5 换件流程补充「A↔B 换屏」:13 号窗改角色 2↔3 + 核对本线 IP
09-25 明确 HMI 固定 IP:换屏必须一起改 HMI IP;13 号窗 HMI IP/掩码/网关(RW-20~31)建议做上
09-25 明确现场不碰 EB:IP 全部在 13 号窗完成;补充 PLC 设备号查看方法(设备列表或显示 LW-9300~9306)