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

1567 lines
111 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-9129`~`9132` / `LW-10750`~`10753` / `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)。
```mermaid
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-9017`~`9022` → `LW-40`~`45`,置 `LB-120` |
| `set-time` | 12 号窗「设定」 | `LW-40`~`45` → `LW-9017`~`9022`(设置 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-40`~`43`;制丸A PLC IP → `RW-44`~`47`;制丸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 110~1900,y 90~1000)**
| # | 区域 | 控件 | 元件 | 地址 | 位置(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-300`~`305`。
11. 「状态设置」给 4 个状态配图/色:`0` 灰(未开始)、`1` 绿(进行中)、`2` 蓝(完成)、`3` 红(异常)。
12. 复制这 6 个到第二组 `y = 560`,地址改 `LW-306`~`311`。
**③ 节点人工修正(显示+可改都用同一地址)**
13. `元件 → 输入 → 数值`,放在每个节点下方:同 `x`、`y = 362`、`100×40`;地址=对应节点状态字(`LW-300`~`305`)。
14. 属性 **「安全」页 → 操作类别 = 类别 A(工程师)**(只有工程师能改);可勾 **「操作前先确认」** 防误改。
15. 第二组同样:`y = 622`,地址 `LW-306`~`311`。
**④ 圆形开关**
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 | 建立窗口骨架与命名规则(X1~X4 × Y1~Y7 → 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`) |