docs(plc): 更新PLC XML配置文件解析与生成说明文档

- Gate 类型从 3 种扩展至 9 种(OR/AND/NOT/T62P/T62D/RISING/FALLING/SR/RS)
- 新增 delayMs、R/S 属性说明
- Comb 新增 id 属性(必填,后序遍历分配)
- 组合链支持链级 Inputs (SR/RS 直接 Input 连接)
- 数据结构扩展: PlcGate 新增 delayMs/rsSSrc/rsRSrc/combId, PlcLink 新增 dstPin
- 解析/生成伪代码同步更新
- 新增 6.3 时序门示例 + 6.4 RS/SR 触发器示例
- 注意事项从 6 条扩展至 12 条
This commit is contained in:
ypc 2026-07-08 13:38:05 +08:00
parent 7d68713f4c
commit 323dc1ced0
1 changed files with 296 additions and 78 deletions

View File

@ -3,7 +3,7 @@
> **面向对象**: QT 上位机开发人员 > **面向对象**: QT 上位机开发人员
> **配置文件**: `config/PLC/Reclose_logic.xml` > **配置文件**: `config/PLC/Reclose_logic.xml`
> **文件编码**: UTF-8 > **文件编码**: UTF-8
> **文档日期**: 2026-07-07 > **文档日期**: 2026-07-08
--- ---
@ -103,38 +103,87 @@ QT 上位机需要实现:
| 属性 | 类型 | 必填 | 说明 | | 属性 | 类型 | 必填 | 说明 |
|------|------|------|------| |------|------|------|------|
| `type` | string | 是 | 逻辑门类型,取值见表(见下方) | | `type` | string | 是 | 逻辑门类型,取值见下表 |
| `id` | int | 是 | 逻辑门序号,从 0 起递增,同类型门内唯一 | | `id` | int | 是 | 同类型门内序号,从 0 起递增,同类型门内唯一 |
| `x` | int | 否 | 可视化画布 X 坐标 | | `x` | int | 否 | 可视化画布 X 坐标 |
| `y` | int | 否 | 可视化画布 Y 坐标 | | `y` | int | 否 | 可视化画布 Y 坐标 |
| `delayMs` | int | 否 | **延迟毫秒数**(仅 `T62P`/`T62D` 有效),默认 1000 |
| `R` | string | 否 | **复位输入来源**(仅 `SR`/`RS` 有效),格式见下方说明 |
| `S` | string | 否 | **置位输入来源**(仅 `SR`/`RS` 有效),格式见下方说明 |
#### 逻辑门类型对照表 #### 逻辑门类型对照表
| `type` 字符串 | 含义 | 输入数 | 说明 | | `type` 字符串 | 含义 | 输入数 | 输出数 | 说明 |
|---------------|------|--------|------| |---------------|------|--------|--------|------|
| `OR` | 或门 | ≥1 | 任一输入为 1 则输出 1 | | `OR` | 或门 | 1~10 | 1~10 | 任一输入为 1 则输出 1 |
| `AND` | 与门 | ≥1 | 全部输入为 1 则输出 1 | | `AND` | 与门 | 1~10 | 1~10 | 全部输入为 1 则输出 1 |
| `NOT` | 非门 | 1 | 输入取反输出 | | `NOT` | 非门 | 1 | 1~10 | 输入取反输出 |
| `T62P` | 62p 延时 | 1 | 1~10 | 输入 1 延时 `delayMs` ms 后输出 1输入 0 立即输出 0 |
| `T62D` | 62d 延时 | 1 | 1~10 | 输入 1 立即输出 1输入 0 延时 `delayMs` ms 后输出 0 |
| `RISING` | 上升沿 | 1 | 1~10 | 0→1 跳变时输出脉冲 |
| `FALLING` | 下降沿 | 1 | 1~10 | 1→0 跳变时输出脉冲 |
| `SR` | SR 触发器 | 2 | 1~10 | S 置位优先。其中引脚顺序为 pin0=S置位pin1=R复位 |
| `RS` | RS 触发器 | 2 | 1~10 | R 复位优先。其中引脚顺序为 pin0=R复位pin1=S置位 |
> **注意**:当前 XML 配置支持的逻辑门类型为 `OR`、`AND`、`NOT`。若后续扩展 `T62P`/`T62D`/`SR`/`RS`/`RISING`/`FALLING` 等时序元件,需同步更新类型对照表。 #### T62P/T62D 延时示例
```xml
<Gate type="T62P" id="0" delayMs="1000" x="-345" y="1063" />
<Gate type="T62D" id="0" delayMs="500" x="-334" y="1185" />
```
#### SR/RS 触发器的 R/S 属性
`R``S` 的值是**来源描述字符串**,格式为 `Comb#N``Input#N`
| 值格式 | 含义 |
|--------|------|
| `Input#59` | 该引脚由输入信号 `no=59` 直接驱动 |
| `Comb#0` | 该引脚由 `id=0` 的 Comb 块内的 Gate 输出驱动 |
```xml
<!-- SR: S 接 Input#13R 接 Input#14 -->
<Gate type="SR" id="0" S="Input#13" R="Input#14" x="-329" y="1631" />
<!-- RS: R 接 Comb#0 的输出S 接 Input#15 -->
<Gate type="RS" id="0" R="Comb#0" S="Input#15" x="0" y="2136" />
```
> **注意**SR/RS 的两个引脚各有独立角色S/R连线时每个引脚只能接一根线。QT 上位机需分别显示两个输入引脚并标注 S/R 标签。
### 3.5 `<Comb>` — 组合逻辑块(核心难点) ### 3.5 `<Comb>` — 组合逻辑块(核心难点)
`<Comb>` 是**可递归嵌套**的逻辑组合块,用于表达多层级的复杂逻辑。 `<Comb>` 是**可递归嵌套**的逻辑组合块,用于表达多层级的复杂逻辑。每个 `<Comb>` 必须有唯一的 `id` 属性。
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `id` | int | 是 | Comb 块唯一标识,从 0 起递增。自底向上分配(最内层 Comb id=0向外递增。用于 RS/SR 引脚引用(`R="Comb#0"` |
```xml ```xml
<Comb> <Comb id="2"> <!-- 最外层 Comb -->
<Inputs> <!-- 本层直接输入信号 --> <Inputs> <!-- 本层直接输入信号 -->
<Signal no="67" x="-6" y="937" /> <Signal no="67" x="-6" y="937" />
</Inputs> </Inputs>
<Comb> <!-- 嵌套子Comb可多层 --> <Comb id="1"> <!-- 嵌套子层 -->
<Inputs> ... </Inputs> <Inputs>
<Comb> ... </Comb> <Signal no="66" x="-333" y="876" />
<Gate type="AND" id="0" .../> </Inputs>
<Outputs> ... </Outputs> <Comb id="0"> <!-- 最内层 -->
<Inputs>
<Signal no="62" x="-669" y="413" />
</Inputs>
<Gate type="AND" id="0" x="-346" y="595" />
<Outputs>
<Signal no="1363" x="-9" y="490" />
</Outputs>
</Comb> </Comb>
<Gate type="OR" id="1" .../> <!-- 本层逻辑门 --> <Gate type="AND" id="1" x="-42" y="829" />
<Outputs> <!-- 本层输出信号 --> <Outputs>
<Signal no="1366" x="324" y="773" />
</Outputs>
</Comb>
<Gate type="OR" id="1" x="288" y="890" />
<Outputs>
<Signal no="1367" x="627" y="836" /> <Signal no="1367" x="627" y="836" />
</Outputs> </Outputs>
</Comb> </Comb>
@ -146,6 +195,7 @@ QT 上位机需要实现:
2. **嵌套子 Comb**:子 Comb 的 Gate 输出 → 连到本层 Gate 的输入端(一根线) 2. **嵌套子 Comb**:子 Comb 的 Gate 输出 → 连到本层 Gate 的输入端(一根线)
3. **本层 Gate**:接收上述所有输入,运算后输出到 Outputs 3. **本层 Gate**:接收上述所有输入,运算后输出到 Outputs
4. **递归终止**:最内层 Comb 没有嵌套子 Comb只有 Inputs → Gate → Outputs 4. **递归终止**:最内层 Comb 没有嵌套子 Comb只有 Inputs → Gate → Outputs
5. **Comb id 分配**:后序遍历(自底向上),最内层 id=0向外逐层递增
**数据流示意** **数据流示意**
@ -155,6 +205,36 @@ QT 上位机需要实现:
[子Comb Gate] ─┘ [子Comb Gate] ─┘
``` ```
### 3.6 组合链中的链级元素
与之前不同,组合链现已支持在 `<Comb>` 块与链级 `<Gate>` 之间插入链级 `<Inputs>`,用于 SR/RS 触发器等需要同时接收 Comb 输出和直接 Input 信号的场景:
```xml
<Chain gates="2" inputs="4" outputs="3">
<Comb id="0">
<Inputs>
<Signal no="10" x="-665" y="1804" />
<Signal no="11" x="-671" y="1932" />
<Signal no="12" x="-671" y="2054" />
</Inputs>
<Gate type="AND" id="2" x="-322" y="1938" />
<Outputs>
<Signal no="1374" x="18" y="1881" />
<Signal no="1375" x="23" y="2004" />
</Outputs>
</Comb>
<Inputs> <!-- 链级输入:直接连到链级 Gate -->
<Signal no="15" x="0" y="2136" />
</Inputs>
<Gate type="RS" id="0" x="0" y="2136" R="Comb#0" S="Input#15" />
<Outputs>
<Signal no="1376" x="375" y="2131" />
</Outputs>
</Chain>
```
**组合链子元素顺序**`<Comb>` → `<Inputs>`(可选)→ `<Gate>``<Outputs>`
--- ---
## 四、完整解析算法C++ / Qt ## 四、完整解析算法C++ / Qt
@ -171,8 +251,13 @@ QT 上位机需要实现:
enum GateType { enum GateType {
GATE_OR = 2, GATE_OR = 2,
GATE_AND = 3, GATE_AND = 3,
GATE_NOT = 4 GATE_NOT = 4,
// 未来可扩展: GATE_T62P=5, GATE_T62D=6, ... GATE_T62P = 5, // 62p 延时
GATE_T62D = 6, // 62d 延时
GATE_RISING = 7, // 上升沿
GATE_SR = 8, // SR 触发器
GATE_RS = 9, // RS 触发器
GATE_FALLING = 10 // 下降沿
}; };
// 信号节点 // 信号节点
@ -186,18 +271,24 @@ struct PlcGate {
GateType type; GateType type;
int id; // 同类型门内序号 int id; // 同类型门内序号
int x, y; // 画布坐标 int x, y; // 画布坐标
int delayMs; // 延时毫秒数(仅 T62P/T62D默认 1000
QString rsSSrc; // S 引脚来源(仅 SR/RS如 "Input#13"、"Comb#0"
QString rsRSrc; // R 引脚来源(仅 SR/RS
int combId; // 所在 Comb 块的 id用于生成 R/S 引用)
}; };
// 连线 // 连线
struct PlcLink { struct PlcLink {
int srcType; // 源节点类型: 1=输入信号, 2~4=逻辑门 int srcType; // 源节点类型编码
int srcId; // 源节点 ID int srcId; // 源节点 ID
int dstType; // 目标节点类型: 0=输出信号, 2~4=逻辑门 int dstType; // 目标节点类型编码
int dstId; // 目标节点 ID int dstId; // 目标节点 ID
int dstPin; // 目标输入引脚序号(仅 SR/RS 有效SR: 0=S/1=R, RS: 0=R/1=S; 其他门默认 -1
}; };
// Comb 组合块(递归结构) // Comb 组合块
struct PlcComb { struct PlcComb {
int id; // Comb 块标识(自底向上编号)
QVector<PlcSignal> inputs; // 本层输入信号 QVector<PlcSignal> inputs; // 本层输入信号
QVector<PlcComb> subCombs; // 嵌套子 Comb QVector<PlcComb> subCombs; // 嵌套子 Comb
PlcGate gate; // 本层逻辑门 PlcGate gate; // 本层逻辑门
@ -211,9 +302,9 @@ struct PlcChain {
int outputsCount; // outputs 属性 int outputsCount; // outputs 属性
bool isComb; // true=组合链, false=简单链 bool isComb; // true=组合链, false=简单链
QVector<PlcSignal> inputs; // (简单链) 输入信号 QVector<PlcSignal> inputs; // (简单链/组合链) 链级输入信号
PlcGate gate; // (简单链) 逻辑门 QVector<PlcComb> topCombs; // (组合链) 顶层 Comb 列表
PlcComb comb; // (组合链) 顶层Comb PlcGate gate; // 链级逻辑门组合链的顶层Gate或简单链的Gate
QVector<PlcSignal> outputs; // 输出信号 QVector<PlcSignal> outputs; // 输出信号
}; };
@ -238,6 +329,12 @@ static GateType parseGateType(const QString &s) {
if (s == "OR") return GATE_OR; if (s == "OR") return GATE_OR;
if (s == "AND") return GATE_AND; if (s == "AND") return GATE_AND;
if (s == "NOT") return GATE_NOT; if (s == "NOT") return GATE_NOT;
if (s == "T62P") return GATE_T62P;
if (s == "T62D") return GATE_T62D;
if (s == "RISING") return GATE_RISING;
if (s == "SR") return GATE_SR;
if (s == "RS") return GATE_RS;
if (s == "FALLING") return GATE_FALLING;
return GATE_OR; // 默认 return GATE_OR; // 默认
} }
@ -247,6 +344,12 @@ static QString gateTypeToString(GateType t) {
case GATE_OR: return "OR"; case GATE_OR: return "OR";
case GATE_AND: return "AND"; case GATE_AND: return "AND";
case GATE_NOT: return "NOT"; case GATE_NOT: return "NOT";
case GATE_T62P: return "T62P";
case GATE_T62D: return "T62D";
case GATE_RISING: return "RISING";
case GATE_SR: return "SR";
case GATE_RS: return "RS";
case GATE_FALLING: return "FALLING";
} }
return "OR"; return "OR";
} }
@ -267,6 +370,10 @@ static PlcGate parseGate(const QDomElement &el) {
g.id = el.attribute("id").toInt(); g.id = el.attribute("id").toInt();
g.x = el.attribute("x").toInt(); g.x = el.attribute("x").toInt();
g.y = el.attribute("y").toInt(); g.y = el.attribute("y").toInt();
g.delayMs = el.attribute("delayMs", "1000").toInt(); // 默认 1000ms
g.rsSSrc = el.attribute("S", ""); // SR/RS 的 S 来源
g.rsRSrc = el.attribute("R", ""); // SR/RS 的 R 来源
g.combId = -1; // 由解析过程赋值
return g; return g;
} }
@ -289,6 +396,9 @@ static QVector<PlcSignal> parseSignalList(const QDomElement &parent,
static bool parseComb(const QDomElement &combEl, PlcComb &comb) { static bool parseComb(const QDomElement &combEl, PlcComb &comb) {
if (combEl.isNull()) return false; if (combEl.isNull()) return false;
// 0. 读取 Comb id 属性
comb.id = combEl.attribute("id", "0").toInt();
// 1. 本层直接输入 // 1. 本层直接输入
comb.inputs = parseSignalList(combEl, "Inputs"); comb.inputs = parseSignalList(combEl, "Inputs");
@ -306,6 +416,7 @@ static bool parseComb(const QDomElement &combEl, PlcComb &comb) {
QDomElement gateEl = combEl.firstChildElement("Gate"); QDomElement gateEl = combEl.firstChildElement("Gate");
if (!gateEl.isNull()) { if (!gateEl.isNull()) {
comb.gate = parseGate(gateEl); comb.gate = parseGate(gateEl);
comb.gate.combId = comb.id; // 回填所在 Comb 的 id
} }
// 4. 本层输出 // 4. 本层输出
@ -325,22 +436,23 @@ static PlcChain parseChain(const QDomElement &chainEl) {
chain.isComb = !combEl.isNull(); chain.isComb = !combEl.isNull();
if (chain.isComb) { if (chain.isComb) {
// 组合链: 解析顶层 Comb // 组合链: 解析所有顶层 Comb可能有多个
parseComb(combEl, chain.comb); QDomElement cEl = combEl;
while (!cEl.isNull()) {
// 链级顶层 Gate在 Comb 之后) PlcComb comb;
QDomNodeList children = chainEl.childNodes(); parseComb(cEl, comb);
bool foundComb = false; chain.topCombs.append(comb);
for (int i = 0; i < children.count(); i++) { cEl = cEl.nextSiblingElement("Comb");
QDomElement el = children.at(i).toElement();
if (el.tagName() == "Comb") { foundComb = true; continue; }
if (foundComb && el.tagName() == "Gate") {
chain.gate = parseGate(el);
break;
}
} }
// 链级顶层 Outputs // 链级 InputsComb 之后Gate 之前,如 RS/SR 的直接 Input 连接)
chain.inputs = parseSignalList(chainEl, "Inputs");
// 链级 Gate
QDomElement gEl = chainEl.firstChildElement("Gate");
if (!gEl.isNull()) chain.gate = parseGate(gEl);
// 链级 Outputs
chain.outputs = parseSignalList(chainEl, "Outputs"); chain.outputs = parseSignalList(chainEl, "Outputs");
} else { } else {
// 简单链: Inputs → Gate → Outputs // 简单链: Inputs → Gate → Outputs
@ -422,6 +534,18 @@ static QDomElement createGateEl(QDomDocument &doc, const PlcGate &gate) {
el.setAttribute("id", gate.id); el.setAttribute("id", gate.id);
el.setAttribute("x", gate.x); el.setAttribute("x", gate.x);
el.setAttribute("y", gate.y); el.setAttribute("y", gate.y);
// T62P/T62D: 延时毫秒
if (gate.type == GATE_T62P || gate.type == GATE_T62D) {
el.setAttribute("delayMs", gate.delayMs);
}
// SR/RS: 输出 R/S 属性(格式 "Comb#N" 或 "Input#N"
if (gate.type == GATE_SR || gate.type == GATE_RS) {
if (!gate.rsSSrc.isEmpty()) el.setAttribute("S", gate.rsSSrc);
if (!gate.rsRSrc.isEmpty()) el.setAttribute("R", gate.rsRSrc);
}
return el; return el;
} }
@ -439,6 +563,7 @@ static QDomElement createSignalListEl(QDomDocument &doc,
// 递归生成 <Comb> 元素 // 递归生成 <Comb> 元素
static QDomElement createCombEl(QDomDocument &doc, const PlcComb &comb) { static QDomElement createCombEl(QDomDocument &doc, const PlcComb &comb) {
QDomElement el = doc.createElement("Comb"); QDomElement el = doc.createElement("Comb");
el.setAttribute("id", comb.id); // Comb id 属性
// Inputs // Inputs
if (!comb.inputs.isEmpty()) { if (!comb.inputs.isEmpty()) {
@ -469,14 +594,17 @@ static QDomElement createChainEl(QDomDocument &doc, const PlcChain &chain) {
el.setAttribute("outputs", chain.outputsCount); el.setAttribute("outputs", chain.outputsCount);
if (chain.isComb) { if (chain.isComb) {
// 组合链: Comb + Gate + Outputs // 组合链: 所有顶层 Comb
el.appendChild(createCombEl(doc, chain.comb)); for (const auto &comb : chain.topCombs) {
// Gate仅在 Comb 后有意义时输出) el.appendChild(createCombEl(doc, comb));
if (chain.gate.type == GATE_OR ||
chain.gate.type == GATE_AND ||
chain.gate.type == GATE_NOT) {
el.appendChild(createGateEl(doc, chain.gate));
} }
// 链级 Inputs如有直接连接如 RS/SR 的 Input 直连)
if (!chain.inputs.isEmpty()) {
el.appendChild(createSignalListEl(doc, "Inputs", chain.inputs));
}
// 链级 Gate
el.appendChild(createGateEl(doc, chain.gate));
// 链级 Outputs
if (!chain.outputs.isEmpty()) { if (!chain.outputs.isEmpty()) {
el.appendChild(createSignalListEl(doc, "Outputs", chain.outputs)); el.appendChild(createSignalListEl(doc, "Outputs", chain.outputs));
} }
@ -578,15 +706,15 @@ bool writeXmlFile(const PlcConfig &config, const QString &filePath,
<!-- 链级: 外层 Gate(OR) 做 NOT → 输出到 1368 --> <!-- 链级: 外层 Gate(OR) 做 NOT → 输出到 1368 -->
<Chain gates="4" inputs="6" outputs="6"> <Chain gates="4" inputs="6" outputs="6">
<Comb> <Comb id="2">
<Inputs> <Inputs>
<Signal no="67" x="-6" y="937" /> <Signal no="67" x="-6" y="937" />
</Inputs> </Inputs>
<Comb> <Comb id="1">
<Inputs> <Inputs>
<Signal no="66" x="-333" y="876" /> <Signal no="66" x="-333" y="876" />
</Inputs> </Inputs>
<Comb> <Comb id="0">
<Inputs> <Inputs>
<Signal no="62" x="-669" y="413" /> <Signal no="62" x="-669" y="413" />
<Signal no="63" x="-670" y="534" /> <Signal no="63" x="-670" y="534" />
@ -625,6 +753,93 @@ bool writeXmlFile(const PlcConfig &config, const QString &filePath,
链级 Gate(NOT id=1): out[1368] = NOT 外层_Gate 链级 Gate(NOT id=1): out[1368] = NOT 外层_Gate
``` ```
### 6.3 时序门简单链示例
```xml
<!-- 62p 延时: 输入保持 1000ms 后才输出 1输入 0 立即输出 0 -->
<Chain gates="1" inputs="1" outputs="1">
<Inputs>
<Signal no="1368" x="-669" y="1065" />
</Inputs>
<Gate type="T62P" id="0" delayMs="1000" x="-345" y="1063" />
<Outputs>
<Signal no="1369" x="-10" y="1061" />
</Outputs>
</Chain>
<!-- 62d 延时: 输入 1 立即输出 1输入 0 延时 500ms 后输出 0 -->
<Chain gates="1" inputs="1" outputs="1">
<Inputs>
<Signal no="1368" x="-669" y="1065" />
</Inputs>
<Gate type="T62D" id="0" delayMs="500" x="-334" y="1185" />
<Outputs>
<Signal no="1369" x="-10" y="1061" />
</Outputs>
</Chain>
<!-- 上升沿: 0→1 跳变时输出脉冲 -->
<Chain gates="1" inputs="1" outputs="1">
<Inputs>
<Signal no="1368" x="-669" y="1065" />
</Inputs>
<Gate type="RISING" id="0" x="-336" y="1305" />
<Outputs>
<Signal no="1369" x="-10" y="1061" />
</Outputs>
</Chain>
<!-- 下降沿: 1→0 跳变时输出脉冲 -->
<Chain gates="1" inputs="1" outputs="1">
<Inputs>
<Signal no="1368" x="-669" y="1065" />
</Inputs>
<Gate type="FALLING" id="0" x="-335" y="1418" />
<Outputs>
<Signal no="1369" x="-10" y="1061" />
</Outputs>
</Chain>
```
### 6.4 RS/SR 触发器示例
```xml
<!-- RS 触发器 (R 复位优先): R 接 Comb#0 输出S 接 Input#15 直连信号 -->
<Chain gates="2" inputs="4" outputs="3">
<Comb id="0">
<Inputs>
<Signal no="10" x="-665" y="1804" />
<Signal no="11" x="-671" y="1932" />
<Signal no="12" x="-671" y="2054" />
</Inputs>
<Gate type="AND" id="2" x="-322" y="1938" />
<Outputs>
<Signal no="1374" x="18" y="1881" />
<Signal no="1375" x="23" y="2004" />
</Outputs>
</Comb>
<Inputs>
<Signal no="15" x="0" y="2136" />
</Inputs>
<Gate type="RS" id="0" x="0" y="2136" R="Comb#0" S="Input#15" />
<Outputs>
<Signal no="1376" x="375" y="2131" />
</Outputs>
</Chain>
<!-- SR 触发器 (S 置位优先): S 接 Input#13R 接 Input#14 -->
<Chain gates="1" inputs="2" outputs="1">
<Inputs>
<Signal no="13" x="-664" y="1545" />
<Signal no="14" x="-665" y="1679" />
</Inputs>
<Gate type="SR" id="0" x="-329" y="1631" S="Input#13" R="Input#14" />
<Outputs>
<Signal no="1377" x="14" y="1628" />
</Outputs>
</Chain>
```
--- ---
## 七、信号编号no的获取 ## 七、信号编号no的获取
@ -664,15 +879,18 @@ RTU 返回 out 信号数组,每条信号包含:
| 编号 | 注意点 | | 编号 | 注意点 |
|------|--------| |------|--------|
| 1 | `<Comb>` 最多嵌套 **3 层**(见示例),更深层级需验证 RTU 固件支持 | | 1 | `<Comb>` 最多嵌套 **3 层**(见示例),更深层级需验证 RTU 固件支持。**每个 Comb 必须有唯一 `id` 属性**,后序遍历自底向上从 0 开始分配 |
| 2 | `checksum` 必须使用 CRC-32/MPEG-2 算法,且大写十六进制格式 | | 2 | `checksum` 必须使用 CRC-32/MPEG-2 算法,且大写十六进制格式 |
| 3 | `key` 建议使用 `QDateTime::currentMSecsSinceEpoch()` 生成,保证唯一性 | | 3 | `key` 建议使用 `QDateTime::currentMSecsSinceEpoch()` 生成,保证唯一性 |
| 4 | `x`/`y` 坐标可不填写(设为 0RTU 解析时忽略这些字段 | | 4 | `x`/`y` 坐标可不填写(设为 0RTU 解析时忽略这些字段 |
| 5 | XML 文件编码必须为 **UTF-8**,不要使用 UTF-8 BOM | | 5 | XML 文件编码必须为 **UTF-8**,不要使用 UTF-8 BOM |
| 6 | `<Chain>``gates`/`inputs`/`outputs` 属性值为统计数量,生成时必须准确填写 | | 6 | `<Chain>``gates`/`inputs`/`outputs` 属性值为统计数量,生成时必须准确填写 |
| 7 | Comb 内 Gate 的 `id` 表示**同类型逻辑门内的序号**OR/AND/NOT 分别计数,从 0 起递增) | | 7 | **T62P/T62D**`delayMs` 为可选属性,默认 1000ms。范围 0~600000 |
| 8 | 生成 XML 后建议通过 RTU Web 界面或命令行 `plc cfg reload` 触发配置重新加载 | | 8 | **SR/RS 触发器**有两个固定输入引脚SR 的 pin0=S(置位)/pin1=R(复位)RS 的 pin0=R(复位)/pin1=S(置位)。每个引脚只能接一根线 |
| 9 | 一个 `<PLCConfig>` 可含多条 `<Chain>`,但目前 RTU 将所有 Chain 合并为一个逻辑图处理 | | 9 | **SR/RS 的 R/S 属性**仅在 Gate 上输出,格式为 `Comb#N``Input#N`。若 RS/SR 所在链为组合链且有直接 Input 连接,需在链级 `<Inputs>` 块中包含对应信号 |
| 10 | 输入信号 `no` 必须是 datacenter out 信号表中的有效序号。QT 上位机需通过 WebSocket 动态获取信号列表,不可硬编码号段 |
| 11 | Comb 内 Gate 的 `id` 表示**同类型逻辑门内的序号**OR/AND/NOT/T62P/T62D/... 分别计数,从 0 起递增) |
| 12 | 一个 `<PLCConfig>` 可含多条 `<Chain>`,目前 RTU 将所有 Chain 合并为一个逻辑图处理 |
--- ---
@ -704,15 +922,15 @@ RTU 返回 out 信号数组,每条信号包含:
</Chain> </Chain>
<!-- 链3: 三层嵌套组合逻辑 --> <!-- 链3: 三层嵌套组合逻辑 -->
<Chain gates="4" inputs="6" outputs="6"> <Chain gates="4" inputs="6" outputs="6">
<Comb> <Comb id="2">
<Inputs> <Inputs>
<Signal no="67" x="-6" y="937" /> <Signal no="67" x="-6" y="937" />
</Inputs> </Inputs>
<Comb> <Comb id="1">
<Inputs> <Inputs>
<Signal no="66" x="-333" y="876" /> <Signal no="66" x="-333" y="876" />
</Inputs> </Inputs>
<Comb> <Comb id="0">
<Inputs> <Inputs>
<Signal no="62" x="-669" y="413" /> <Signal no="62" x="-669" y="413" />
<Signal no="63" x="-670" y="534" /> <Signal no="63" x="-670" y="534" />