feat(plc): PLC逻辑配置完整功能实现
前端 plc_config.js: - NODE_INFO 输入/输出限制修正 (AND/OR ≤10, NOT ≤1, 62P/62D/沿 ≤1, SR/RS ≤2) - delayMs 独立字段,不再编码到 id 高位 - SR/RS 双引脚标注 (S 黄色/R 红色),各引脚只能接一条线 - Comb id 解析与生成 (<Comb id="N">) - Gate R/S 属性生成 (S="Comb#N"/"Input#N" 格式) - 延时门新增/双击编辑自定义 modal - 新节点自动空位放置 - XML 链级 Inputs 自动输出 (RS/SR 直接 Input 连接) - 解析补全: 链级 Gate 读 R/S/delayMs,dstPin 修复 - RS/SR R/S 属性→Input#N 链接自动创建 后端 plc.cpp: - LogicNode 新增 delay_ms, rs_s_src[32], rs_r_src[32] - LogicLink 新增 dst_pin 字段 - parse_xml_comb: 读 delayMs→delay_ms, R/S→链接+dst_pin - execute_62p/62d 改用 node->delay_ms - collect_input_signals 按 dst_pin 填入 - has_comb 路径 R/S 属性→Input 链接创建 配置文件 Reclose_logic.xml 更新为完整示例
This commit is contained in:
parent
780b8cc311
commit
7d68713f4c
|
|
@ -71,6 +71,9 @@ typedef struct {
|
|||
struct timespec timer_start; // 定时器启动时刻
|
||||
int timer_delay_ms; // 延时毫秒数
|
||||
int timer_pending_output; // 定时器到期后的输出值,-1=无待定输出
|
||||
int delay_ms; // 配置的延时毫秒数(62p/62d,从XML delayMs属性读取)
|
||||
char rs_s_src[32]; // S引脚来源描述(SR/RS)
|
||||
char rs_r_src[32]; // R引脚来源描述(SR/RS)
|
||||
// 拓扑排序字段
|
||||
int indegree; // 入度(依赖的前驱节点数)
|
||||
} LogicNode;
|
||||
|
|
@ -80,6 +83,7 @@ typedef struct {
|
|||
int src_id;
|
||||
int dest_type;
|
||||
int dest_id;
|
||||
int dst_pin; // 目标引脚序号(0=S, 1=R,仅SR/RS有效,-1=默认)
|
||||
} LogicLink;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -297,18 +301,90 @@ static int parse_xml_comb(tinyxml2::XMLElement *comb_el, LogicGraph *graph)
|
|||
gn.id = gate_el->IntAttribute("id");
|
||||
int gi = graph->node_count - 1;
|
||||
|
||||
// 读取延时毫秒(62P/62D)
|
||||
const char *delay_str = gate_el->Attribute("delayMs");
|
||||
if(delay_str) gn.delay_ms = atoi(delay_str);
|
||||
else gn.delay_ms = 1000; // 默认1000ms
|
||||
|
||||
// 读取 SR/RS 的 R/S 属性
|
||||
const char *r_attr = gate_el->Attribute("R");
|
||||
const char *s_attr = gate_el->Attribute("S");
|
||||
if(r_attr) { strncpy(gn.rs_r_src, r_attr, sizeof(gn.rs_r_src) - 1); }
|
||||
if(s_attr) { strncpy(gn.rs_s_src, s_attr, sizeof(gn.rs_s_src) - 1); }
|
||||
|
||||
int is_srrs = (gn.type == NODE_TYPE_SR_LATCH || gn.type == NODE_TYPE_RS_LATCH);
|
||||
|
||||
// 收集嵌套 Comb 的 Gate 索引(按 id 映射,用于 R/S 解析)
|
||||
int sub_gate_by_comb_id[16] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
|
||||
int sub_gate_count = 0;
|
||||
|
||||
for(tinyxml2::XMLElement *nc = comb_el->FirstChildElement("Comb"); nc;
|
||||
nc = nc->NextSiblingElement("Comb"))
|
||||
{
|
||||
int up = parse_xml_comb(nc, graph);
|
||||
if(up >= 0 && graph->link_count < MAX_LINKS_PER_GRAPH)
|
||||
{ LogicLink &l = graph->links[graph->link_count++];
|
||||
l.src_type = graph->nodes[up].type; l.src_id = graph->nodes[up].id;
|
||||
l.dest_type = gn.type; l.dest_id = gn.id; }
|
||||
if(up >= 0)
|
||||
{
|
||||
int cid = nc->IntAttribute("id");
|
||||
if(cid >= 0 && cid < 16) sub_gate_by_comb_id[cid] = up;
|
||||
sub_gate_count++;
|
||||
// 非 SR/RS: 自动创建嵌套 Comb Gate → 当前 Gate 的链接
|
||||
if(!is_srrs && graph->link_count < MAX_LINKS_PER_GRAPH)
|
||||
{
|
||||
LogicLink &l = graph->links[graph->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type = graph->nodes[up].type; l.src_id = graph->nodes[up].id;
|
||||
l.dest_type = gn.type; l.dest_id = gn.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SR/RS: 根据 R/S 属性创建链接
|
||||
if(is_srrs)
|
||||
{
|
||||
// 解析 "Comb#N" 或 "Input#N"
|
||||
for(int pin = 0; pin < 2; pin++)
|
||||
{
|
||||
const char *attr = (pin == 0) ? s_attr : r_attr;
|
||||
if(!attr || graph->link_count >= MAX_LINKS_PER_GRAPH) continue;
|
||||
|
||||
if(strncmp(attr, "Comb#", 5) == 0)
|
||||
{
|
||||
int cid = atoi(attr + 5);
|
||||
if(cid >= 0 && cid < 16 && sub_gate_by_comb_id[cid] >= 0)
|
||||
{
|
||||
int up = sub_gate_by_comb_id[cid];
|
||||
LogicLink &l = graph->links[graph->link_count++];
|
||||
memset(&l, 0, sizeof(l));
|
||||
l.src_type = graph->nodes[up].type; l.src_id = graph->nodes[up].id;
|
||||
l.dest_type = gn.type; l.dest_id = gn.id;
|
||||
// SR: S=pin0, R=pin1; RS: S=pin1, R=pin0
|
||||
if(gn.type == NODE_TYPE_SR_LATCH)
|
||||
l.dst_pin = pin; // 0=S, 1=R
|
||||
else
|
||||
l.dst_pin = 1 - pin; // 0=R, 1=S
|
||||
}
|
||||
}
|
||||
else if(strncmp(attr, "Input#", 6) == 0)
|
||||
{
|
||||
int ino = atoi(attr + 6);
|
||||
int si = ensure_signal(graph, NODE_TYPE_HW_INPUT, ino);
|
||||
if(si >= 0)
|
||||
{
|
||||
LogicLink &l = graph->links[graph->link_count++];
|
||||
memset(&l, 0, sizeof(l));
|
||||
l.src_type = NODE_TYPE_HW_INPUT; l.src_id = graph->nodes[si].id;
|
||||
l.dest_type = gn.type; l.dest_id = gn.id;
|
||||
if(gn.type == NODE_TYPE_SR_LATCH)
|
||||
l.dst_pin = pin;
|
||||
else
|
||||
l.dst_pin = 1 - pin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *ins = comb_el->FirstChildElement("Inputs");
|
||||
if(ins)
|
||||
if(ins && !is_srrs)
|
||||
for(tinyxml2::XMLElement *sig = ins->FirstChildElement("Signal"); sig;
|
||||
sig = sig->NextSiblingElement("Signal"))
|
||||
{
|
||||
|
|
@ -316,6 +392,7 @@ static int parse_xml_comb(tinyxml2::XMLElement *comb_el, LogicGraph *graph)
|
|||
int si = ensure_signal(graph, NODE_TYPE_HW_INPUT, sig->IntAttribute("no"));
|
||||
if(si >= 0)
|
||||
{ LogicLink &l = graph->links[graph->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type=NODE_TYPE_HW_INPUT; l.src_id=graph->nodes[si].id;
|
||||
l.dest_type=gn.type; l.dest_id=gn.id; }
|
||||
}
|
||||
|
|
@ -426,6 +503,7 @@ int parse_reclose_logic_xml(const char *file_path, LogicGraph graphs[], int *gra
|
|||
if(ci >= 0 && cur->link_count < MAX_LINKS_PER_GRAPH)
|
||||
{
|
||||
LogicLink &l = cur->links[cur->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type = cur->nodes[ci].type;
|
||||
l.src_id = cur->nodes[ci].id;
|
||||
l.dest_type = cur->nodes[cgi].type;
|
||||
|
|
@ -449,11 +527,68 @@ int parse_reclose_logic_xml(const char *file_path, LogicGraph graphs[], int *gra
|
|||
int si = find_node_index(cur, NODE_TYPE_HW_OUTPUT, sig->IntAttribute("no"));
|
||||
if(si >= 0)
|
||||
{ LogicLink &l = cur->links[cur->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type=cur->nodes[cgi].type; l.src_id=cur->nodes[cgi].id;
|
||||
l.dest_type=NODE_TYPE_HW_OUTPUT; l.dest_id=cur->nodes[si].id; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 链级 Inputs → 链级 Gate(如 RS/SR 直接连 Input 信号)
|
||||
if(gate_el && ins_el)
|
||||
{
|
||||
int cgt = gate_type_from_xml_str(gate_el->Attribute("type"));
|
||||
int cgi = find_node_index(cur, cgt, gate_el->IntAttribute("id"));
|
||||
if(cgi >= 0)
|
||||
{
|
||||
for(tinyxml2::XMLElement *sig = ins_el->FirstChildElement("Signal"); sig;
|
||||
sig = sig->NextSiblingElement("Signal"))
|
||||
{
|
||||
if(cur->link_count >= MAX_LINKS_PER_GRAPH) break;
|
||||
int si = find_node_index(cur, NODE_TYPE_HW_INPUT, sig->IntAttribute("no"));
|
||||
if(si >= 0)
|
||||
{ LogicLink &l = cur->links[cur->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type=NODE_TYPE_HW_INPUT; l.src_id=cur->nodes[si].id;
|
||||
l.dest_type=cur->nodes[cgi].type; l.dest_id=cur->nodes[cgi].id; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RS/SR 的 R/S 属性引用的 Input 信号(无 <Inputs> 块时也需创建链接)
|
||||
if(gate_el)
|
||||
{
|
||||
int cgt = gate_type_from_xml_str(gate_el->Attribute("type"));
|
||||
if(cgt == NODE_TYPE_SR_LATCH || cgt == NODE_TYPE_RS_LATCH)
|
||||
{
|
||||
const char *s_attr2 = gate_el->Attribute("S");
|
||||
const char *r_attr2 = gate_el->Attribute("R");
|
||||
int cgi2 = find_node_index(cur, cgt, gate_el->IntAttribute("id"));
|
||||
if(cgi2 >= 0)
|
||||
{
|
||||
if(s_attr2 && strncmp(s_attr2, "Input#", 6) == 0)
|
||||
{
|
||||
int ino = atoi(s_attr2 + 6);
|
||||
int si = ensure_signal(cur, NODE_TYPE_HW_INPUT, ino);
|
||||
if(si >= 0 && cur->link_count < MAX_LINKS_PER_GRAPH)
|
||||
{ LogicLink &l = cur->links[cur->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type=NODE_TYPE_HW_INPUT; l.src_id=cur->nodes[si].id;
|
||||
l.dest_type=cur->nodes[cgi2].type; l.dest_id=cur->nodes[cgi2].id; }
|
||||
}
|
||||
if(r_attr2 && strncmp(r_attr2, "Input#", 6) == 0)
|
||||
{
|
||||
int ino = atoi(r_attr2 + 6);
|
||||
int si = ensure_signal(cur, NODE_TYPE_HW_INPUT, ino);
|
||||
if(si >= 0 && cur->link_count < MAX_LINKS_PER_GRAPH)
|
||||
{ LogicLink &l = cur->links[cur->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type=NODE_TYPE_HW_INPUT; l.src_id=cur->nodes[si].id;
|
||||
l.dest_type=cur->nodes[cgi2].type; l.dest_id=cur->nodes[cgi2].id; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -471,6 +606,7 @@ int parse_reclose_logic_xml(const char *file_path, LogicGraph graphs[], int *gra
|
|||
int si = find_node_index(cur, NODE_TYPE_HW_INPUT, sig->IntAttribute("no"));
|
||||
if(si >= 0)
|
||||
{ LogicLink &l = cur->links[cur->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type=NODE_TYPE_HW_INPUT; l.src_id=cur->nodes[si].id;
|
||||
l.dest_type=cur->nodes[gi].type; l.dest_id=cur->nodes[gi].id; }
|
||||
}
|
||||
|
|
@ -483,6 +619,7 @@ int parse_reclose_logic_xml(const char *file_path, LogicGraph graphs[], int *gra
|
|||
int si = find_node_index(cur, NODE_TYPE_HW_OUTPUT, sig->IntAttribute("no"));
|
||||
if(si >= 0)
|
||||
{ LogicLink &l = cur->links[cur->link_count++];
|
||||
memset(&l, 0, sizeof(l)); l.dst_pin = -1;
|
||||
l.src_type=cur->nodes[gi].type; l.src_id=cur->nodes[gi].id;
|
||||
l.dest_type=NODE_TYPE_HW_OUTPUT; l.dest_id=cur->nodes[si].id; }
|
||||
}
|
||||
|
|
@ -505,9 +642,19 @@ static int collect_input_signals(LogicGraph *graph, LogicNode *element_node,
|
|||
((link->dest_id & 0xFF) == (element_node->id & 0xFF))) {
|
||||
LogicNode *src_node = find_node(graph, link->src_type, link->src_id);
|
||||
if (src_node) {
|
||||
input_values[input_count++] = src_node->value;
|
||||
printf(" 找到输入信号:%d:%d = %d\n",
|
||||
link->src_type, link->src_id, src_node->value);
|
||||
// SR/RS: 按 dst_pin 填入对应位置
|
||||
int is_srrs = (element_node->type == NODE_TYPE_SR_LATCH ||
|
||||
element_node->type == NODE_TYPE_RS_LATCH);
|
||||
if(is_srrs && link->dst_pin >= 0) {
|
||||
if(input_count < 2) {
|
||||
input_values[link->dst_pin] = src_node->value;
|
||||
if(link->dst_pin >= input_count) input_count = link->dst_pin + 1;
|
||||
}
|
||||
} else {
|
||||
input_values[input_count++] = src_node->value;
|
||||
}
|
||||
printf(" 找到输入信号:%d:%d = %d (pin=%d)\n",
|
||||
link->src_type, link->src_id, src_node->value, link->dst_pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -544,15 +691,15 @@ static void execute_or_gate(LogicNode *or_node, int *input_values, int input_cou
|
|||
}
|
||||
|
||||
static void execute_62p_gate(LogicNode *node, int input_value) {
|
||||
int tp = (node->id >> 8) & 0xFFFFFF;
|
||||
int delay_ms = node->delay_ms > 0 ? node->delay_ms : 1000;
|
||||
int component_id = node->id & 0xFF;
|
||||
if (input_value == 1) {
|
||||
if (!node->timer_active) {
|
||||
// 启动延时定时器,延时 tp 毫秒后输出 1
|
||||
// 启动延时定时器,延时 delay_ms 毫秒后输出 1
|
||||
printf(" 62p延时元件 (ID=%d, 序号=%d):输入=1,启动非阻塞延时%d毫秒\n",
|
||||
node->id, component_id, tp);
|
||||
node->id, component_id, delay_ms);
|
||||
clock_gettime(CLOCK_MONOTONIC, &node->timer_start);
|
||||
node->timer_delay_ms = tp;
|
||||
node->timer_delay_ms = delay_ms;
|
||||
node->timer_pending_output = 1;
|
||||
node->timer_active = 1;
|
||||
// 保持当前输出值不变,等待定时器到期
|
||||
|
|
@ -574,7 +721,7 @@ static void execute_62p_gate(LogicNode *node, int input_value) {
|
|||
}
|
||||
|
||||
static void execute_62d_gate(LogicNode *node, int input_value) {
|
||||
int td = (node->id >> 8) & 0xFFFFFF;
|
||||
int delay_ms = node->delay_ms > 0 ? node->delay_ms : 1000;
|
||||
int component_id = node->id & 0xFF;
|
||||
if (input_value == 1) {
|
||||
// 输入为 1,立即输出 1 并取消定时器
|
||||
|
|
@ -585,11 +732,11 @@ static void execute_62d_gate(LogicNode *node, int input_value) {
|
|||
node->id, component_id);
|
||||
} else {
|
||||
if (!node->timer_active) {
|
||||
// 启动延时定时器,延时 td 毫秒后输出 0
|
||||
// 启动延时定时器,延时 delay_ms 毫秒后输出 0
|
||||
printf(" 62d延时元件 (ID=%d, 序号=%d):输入=0,启动非阻塞延时%d毫秒\n",
|
||||
node->id, component_id, td);
|
||||
node->id, component_id, delay_ms);
|
||||
clock_gettime(CLOCK_MONOTONIC, &node->timer_start);
|
||||
node->timer_delay_ms = td;
|
||||
node->timer_delay_ms = delay_ms;
|
||||
node->timer_pending_output = 0;
|
||||
node->timer_active = 1;
|
||||
// 保持当前输出值不变,等待定时器到期
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -20,15 +20,15 @@
|
|||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="4" inputs="6" outputs="6">
|
||||
<Comb>
|
||||
<Comb id="2">
|
||||
<Inputs>
|
||||
<Signal no="67" x="-6" y="937" />
|
||||
</Inputs>
|
||||
<Comb>
|
||||
<Comb id="1">
|
||||
<Inputs>
|
||||
<Signal no="66" x="-333" y="876" />
|
||||
</Inputs>
|
||||
<Comb>
|
||||
<Comb id="0">
|
||||
<Inputs>
|
||||
<Signal no="62" x="-669" y="413" />
|
||||
<Signal no="63" x="-670" y="534" />
|
||||
|
|
@ -57,4 +57,71 @@
|
|||
<Signal no="1368" x="915" y="947" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="5" x="-669" y="1065" />
|
||||
</Inputs>
|
||||
<Gate type="T62P" id="0" x="-345" y="1063" delayMs="1000" />
|
||||
<Outputs>
|
||||
<Signal no="1370" x="-10" y="1061" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="6" x="-667" y="1187" />
|
||||
</Inputs>
|
||||
<Gate type="T62D" id="0" x="-334" y="1185" delayMs="1000" />
|
||||
<Outputs>
|
||||
<Signal no="1371" x="-4" y="1186" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="7" x="-670" y="1305" />
|
||||
</Inputs>
|
||||
<Gate type="RISING" id="0" x="-336" y="1305" />
|
||||
<Outputs>
|
||||
<Signal no="1372" x="-3" y="1304" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="8" x="-662" y="1421" />
|
||||
</Inputs>
|
||||
<Gate type="FALLING" id="0" x="-335" y="1418" />
|
||||
<Outputs>
|
||||
<Signal no="1373" x="4" y="1419" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<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="-331" y="2193" />
|
||||
</Inputs>
|
||||
<Gate type="RS" id="0" x="14" y="2133" R="Comb#0" S="Input#15" />
|
||||
<Outputs>
|
||||
<Signal no="1376" x="375" y="2131" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<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>
|
||||
</PLCConfig>
|
||||
|
|
|
|||
|
|
@ -1,60 +1,127 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PLCConfig name="PLC Logic" key="1783070862205" checksum="3486BE04">
|
||||
<Chain gates="1" inputs="2" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="59" x="-676" y="69" />
|
||||
<Signal no="60" x="-674" y="183" />
|
||||
</Inputs>
|
||||
<Gate type="OR" id="0" x="-346" y="141" />
|
||||
<Outputs>
|
||||
<Signal no="1361" x="-17" y="137" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="61" x="-669" y="298" />
|
||||
</Inputs>
|
||||
<Gate type="NOT" id="0" x="-345" y="296" />
|
||||
<Outputs>
|
||||
<Signal no="1362" x="-10" y="294" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="4" inputs="6" outputs="6">
|
||||
<Comb>
|
||||
<Chain gates="1" inputs="2" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="67" x="-6" y="937" />
|
||||
<Signal no="59" x="-676" y="69" />
|
||||
<Signal no="60" x="-674" y="183" />
|
||||
</Inputs>
|
||||
<Comb>
|
||||
<Inputs>
|
||||
<Signal no="66" x="-333" y="876" />
|
||||
</Inputs>
|
||||
<Comb>
|
||||
<Inputs>
|
||||
<Signal no="62" x="-669" y="413" />
|
||||
<Signal no="63" x="-670" y="534" />
|
||||
<Signal no="64" x="-667" y="650" />
|
||||
<Signal no="65" x="-669" y="769" />
|
||||
</Inputs>
|
||||
<Gate type="AND" id="0" x="-346" y="595" />
|
||||
<Outputs>
|
||||
<Signal no="1363" x="-9" y="490" />
|
||||
<Signal no="1364" x="-11" y="599" />
|
||||
<Signal no="1365" x="-10" y="700" />
|
||||
</Outputs>
|
||||
</Comb>
|
||||
<Gate type="AND" id="1" x="-42" y="829" />
|
||||
<Outputs>
|
||||
<Signal no="1366" x="324" y="773" />
|
||||
</Outputs>
|
||||
</Comb>
|
||||
<Gate type="OR" id="1" x="288" y="890" />
|
||||
<Gate type="OR" id="0" x="-346" y="141" />
|
||||
<Outputs>
|
||||
<Signal no="1367" x="627" y="836" />
|
||||
<Signal no="1361" x="-17" y="137" />
|
||||
</Outputs>
|
||||
</Comb>
|
||||
<Gate type="NOT" id="1" x="594" y="950" />
|
||||
<Outputs>
|
||||
<Signal no="1368" x="915" y="947" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="61" x="-669" y="298" />
|
||||
</Inputs>
|
||||
<Gate type="NOT" id="0" x="-345" y="296" />
|
||||
<Outputs>
|
||||
<Signal no="1362" x="-10" y="294" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="4" inputs="6" outputs="6">
|
||||
<Comb id="2">
|
||||
<Inputs>
|
||||
<Signal no="67" x="-6" y="937" />
|
||||
</Inputs>
|
||||
<Comb id="1">
|
||||
<Inputs>
|
||||
<Signal no="66" x="-333" y="876" />
|
||||
</Inputs>
|
||||
<Comb id="0">
|
||||
<Inputs>
|
||||
<Signal no="62" x="-669" y="413" />
|
||||
<Signal no="63" x="-670" y="534" />
|
||||
<Signal no="64" x="-667" y="650" />
|
||||
<Signal no="65" x="-669" y="769" />
|
||||
</Inputs>
|
||||
<Gate type="AND" id="0" x="-346" y="595" />
|
||||
<Outputs>
|
||||
<Signal no="1363" x="-9" y="490" />
|
||||
<Signal no="1364" x="-11" y="599" />
|
||||
<Signal no="1365" x="-10" y="700" />
|
||||
</Outputs>
|
||||
</Comb>
|
||||
<Gate type="AND" id="1" x="-42" y="829" />
|
||||
<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" />
|
||||
</Outputs>
|
||||
</Comb>
|
||||
<Gate type="NOT" id="1" x="594" y="950" />
|
||||
<Outputs>
|
||||
<Signal no="1368" x="915" y="947" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="5" x="-669" y="1065" />
|
||||
</Inputs>
|
||||
<Gate type="T62P" id="0" x="-345" y="1063" delayMs="1000" />
|
||||
<Outputs>
|
||||
<Signal no="1370" x="-10" y="1061" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="6" x="-667" y="1187" />
|
||||
</Inputs>
|
||||
<Gate type="T62D" id="0" x="-334" y="1185" delayMs="1000" />
|
||||
<Outputs>
|
||||
<Signal no="1371" x="-4" y="1186" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="7" x="-670" y="1305" />
|
||||
</Inputs>
|
||||
<Gate type="RISING" id="0" x="-336" y="1305" />
|
||||
<Outputs>
|
||||
<Signal no="1372" x="-3" y="1304" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<Chain gates="1" inputs="1" outputs="1">
|
||||
<Inputs>
|
||||
<Signal no="8" x="-662" y="1421" />
|
||||
</Inputs>
|
||||
<Gate type="FALLING" id="0" x="-335" y="1418" />
|
||||
<Outputs>
|
||||
<Signal no="1373" x="4" y="1419" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<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="-331" y="2193" />
|
||||
</Inputs>
|
||||
<Gate type="RS" id="0" x="14" y="2133" R="Comb#0" S="Input#15" />
|
||||
<Outputs>
|
||||
<Signal no="1376" x="375" y="2131" />
|
||||
</Outputs>
|
||||
</Chain>
|
||||
<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>
|
||||
</PLCConfig>
|
||||
|
|
|
|||
|
|
@ -62,15 +62,15 @@ var PlcConfigPage = (function() {
|
|||
var NODE_INFO = {
|
||||
0: { name: '输出点', cls: 'node-out', icon: '📤', color: '#4CAF50', inPins: 1, outPins: 0, inMax: 1, outMax: 0 },
|
||||
1: { name: '输入点', cls: 'node-in', icon: '📥', color: '#2196F3', inPins: 0, outPins: 1, inMax: 0, outMax: 99 },
|
||||
2: { name: '或门 OR', cls: 'node-or', icon: '🔶', color: '#FF9800', inPins: 2, outPins: 1, inMax: 99, outMax: 10 },
|
||||
3: { name: '与门 AND', cls: 'node-and', icon: '🔷', color: '#9C27B0', inPins: 2, outPins: 1, inMax: 99, outMax: 10 },
|
||||
2: { name: '或门 OR', cls: 'node-or', icon: '🔶', color: '#FF9800', inPins: 2, outPins: 1, inMax: 10, outMax: 10 },
|
||||
3: { name: '与门 AND', cls: 'node-and', icon: '🔷', color: '#9C27B0', inPins: 2, outPins: 1, inMax: 10, outMax: 10 },
|
||||
4: { name: '非门 NOT', cls: 'node-not', icon: '🔺', color: '#f44336', inPins: 1, outPins: 1, inMax: 1, outMax: 10 },
|
||||
5: { name: '62p延时', cls: 'node-62p', icon: '⏱️', color: '#00BCD4', inPins: 1, outPins: 1, inMax: 1, outMax: 1 },
|
||||
6: { name: '62d延时', cls: 'node-62d', icon: '⏱️', color: '#009688', inPins: 1, outPins: 1, inMax: 1, outMax: 1 },
|
||||
7: { name: '上升沿', cls: 'node-re', icon: '⬆️', color: '#E91E63', inPins: 1, outPins: 1, inMax: 1, outMax: 1 },
|
||||
8: { name: 'SR触发器', cls: 'node-sr', icon: '🔒', color: '#795548', inPins: 2, outPins: 1, inMax: 2, outMax: 1 },
|
||||
9: { name: 'RS触发器', cls: 'node-rs', icon: '🔒', color: '#607D8B', inPins: 2, outPins: 1, inMax: 2, outMax: 1 },
|
||||
10: { name: '下降沿', cls: 'node-fe', icon: '⬇️', color: '#FF5722', inPins: 1, outPins: 1, inMax: 1, outMax: 1 }
|
||||
5: { name: '62p延时', cls: 'node-62p', icon: '⏱️', color: '#00BCD4', inPins: 1, outPins: 1, inMax: 1, outMax: 10 },
|
||||
6: { name: '62d延时', cls: 'node-62d', icon: '⏱️', color: '#009688', inPins: 1, outPins: 1, inMax: 1, outMax: 10 },
|
||||
7: { name: '上升沿', cls: 'node-re', icon: '⬆️', color: '#E91E63', inPins: 1, outPins: 1, inMax: 1, outMax: 10 },
|
||||
8: { name: 'SR触发器', cls: 'node-sr', icon: '🔒', color: '#795548', inPins: 2, outPins: 1, inMax: 2, outMax: 10 },
|
||||
9: { name: 'RS触发器', cls: 'node-rs', icon: '🔒', color: '#607D8B', inPins: 2, outPins: 1, inMax: 2, outMax: 10 },
|
||||
10: { name: '下降沿', cls: 'node-fe', icon: '⬇️', color: '#FF5722', inPins: 1, outPins: 1, inMax: 1, outMax: 10 }
|
||||
};
|
||||
|
||||
/* ========== 工具函数 ========== */
|
||||
|
|
@ -401,7 +401,7 @@ var PlcConfigPage = (function() {
|
|||
if (g.nodes[i].type === type && (g.nodes[i].id & 0xFF) === (id & 0xFF)) return i;
|
||||
var nd = { type:type, id:id, x:x||0, y:y||0 };
|
||||
if (inPins) nd.inPins = inPins;
|
||||
if (delayMs) nd.id = (delayMs << 8) | nd.id;
|
||||
if (delayMs) nd.delayMs = delayMs;
|
||||
g.nodes.push(nd);
|
||||
return g.nodes.length - 1;
|
||||
}
|
||||
|
|
@ -417,6 +417,16 @@ var PlcConfigPage = (function() {
|
|||
ge.getAttribute('inPins') ? parseInt(ge.getAttribute('inPins')) : null,
|
||||
ge.getAttribute('delayMs') ? parseInt(ge.getAttribute('delayMs')) : null);
|
||||
|
||||
// 读取 Comb id
|
||||
var combId = combEl.getAttribute('id');
|
||||
if (combId) g.nodes[gi].combId = parseInt(combId);
|
||||
|
||||
// 读取 SR/RS 的 R/S 属性
|
||||
var rAttr = ge.getAttribute('R');
|
||||
var sAttr = ge.getAttribute('S');
|
||||
if (rAttr) g.nodes[gi].rsR = rAttr;
|
||||
if (sAttr) g.nodes[gi].rsS = sAttr;
|
||||
|
||||
var c = combEl.firstElementChild;
|
||||
while (c) {
|
||||
if (c.nodeName === 'Comb') {
|
||||
|
|
@ -482,8 +492,14 @@ var PlcConfigPage = (function() {
|
|||
if (c.nodeName === 'Comb') parseComb(c);
|
||||
if (c.nodeName === 'Gate') {
|
||||
var gt = GTM[c.getAttribute('type')] || 0;
|
||||
ensureGate(gt, parseInt(c.getAttribute('id')),
|
||||
var giChain = ensureGate(gt, parseInt(c.getAttribute('id')),
|
||||
parseInt(c.getAttribute('x')), parseInt(c.getAttribute('y')));
|
||||
var rAttrC = c.getAttribute('R');
|
||||
var sAttrC = c.getAttribute('S');
|
||||
var dmsC = c.getAttribute('delayMs');
|
||||
if (rAttrC) g.nodes[giChain].rsR = rAttrC;
|
||||
if (sAttrC) g.nodes[giChain].rsS = sAttrC;
|
||||
if (dmsC) g.nodes[giChain].delayMs = parseInt(dmsC);
|
||||
}
|
||||
c = c.nextElementSibling;
|
||||
}
|
||||
|
|
@ -519,6 +535,29 @@ var PlcConfigPage = (function() {
|
|||
}
|
||||
c3 = c3.nextElementSibling;
|
||||
}
|
||||
// 链级 Inputs(如 RS/SR 直接连 Input 信号)→ 链级 Gate
|
||||
if (ie2) {
|
||||
var s = ie2.firstElementChild;
|
||||
while (s) { if (s.nodeName === 'Signal') g.links.push({ src_type:NT_INPUT, src_id:parseInt(s.getAttribute('no')), dst_type:GTM[ge2.getAttribute('type')] || 0, dst_id:parseInt(ge2.getAttribute('id')) }); s = s.nextElementSibling; }
|
||||
}
|
||||
// RS/SR 的 R/S 属性引用的 Input 信号(无 <Inputs> 块时也需创建链接)
|
||||
if (ge2) {
|
||||
var gtCS = GTM[ge2.getAttribute('type')] || 0;
|
||||
if (gtCS === NT_SR || gtCS === NT_RS) {
|
||||
var sAttrCS = ge2.getAttribute('S');
|
||||
var rAttrCS = ge2.getAttribute('R');
|
||||
if (sAttrCS && sAttrCS.indexOf('Input#') === 0) {
|
||||
var sNo = parseInt(sAttrCS.substring(6));
|
||||
ensureSig(NT_INPUT, sNo, 0, 0);
|
||||
g.links.push({ src_type:NT_INPUT, src_id:sNo, dst_type:gtCS, dst_id:parseInt(ge2.getAttribute('id')) });
|
||||
}
|
||||
if (rAttrCS && rAttrCS.indexOf('Input#') === 0) {
|
||||
var rNo = parseInt(rAttrCS.substring(6));
|
||||
ensureSig(NT_INPUT, rNo, 0, 0);
|
||||
g.links.push({ src_type:NT_INPUT, src_id:rNo, dst_type:gtCS, dst_id:parseInt(ge2.getAttribute('id')) });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oe2) {
|
||||
var s = oe2.firstElementChild;
|
||||
while (s) { if (s.nodeName === 'Signal') g.links.push({ src_type:GTM[ge2.getAttribute('type')] || 0, src_id:parseInt(ge2.getAttribute('id')), dst_type:NT_OUTPUT, dst_id:parseInt(s.getAttribute('no')) }); s = s.nextElementSibling; }
|
||||
|
|
@ -537,6 +576,38 @@ var PlcConfigPage = (function() {
|
|||
}
|
||||
}
|
||||
|
||||
// 最终遍:为 SR/RS 节点修复 dstPin
|
||||
for (var ni = 0; ni < g.nodes.length; ni++) {
|
||||
var nd = g.nodes[ni];
|
||||
if (nd.type !== NT_SR && nd.type !== NT_RS) continue;
|
||||
var sSrc = nd.rsS || '';
|
||||
var rSrc = nd.rsR || '';
|
||||
if (!sSrc && !rSrc) continue;
|
||||
// 遍历所有到该节点的链路
|
||||
for (var lj = 0; lj < g.links.length; lj++) {
|
||||
var lk = g.links[lj];
|
||||
if (lk.dst_type !== nd.type || (lk.dst_id & 0xFF) !== (nd.id & 0xFF)) continue;
|
||||
var srcDesc = '';
|
||||
var sN = null;
|
||||
for (var sn = 0; sn < g.nodes.length; sn++) {
|
||||
if (g.nodes[sn].type === lk.src_type &&
|
||||
(g.nodes[sn].type <= 1 ? g.nodes[sn].id === lk.src_id : (g.nodes[sn].id & 0xFF) === (lk.src_id & 0xFF))) {
|
||||
sN = g.nodes[sn]; break;
|
||||
}
|
||||
}
|
||||
if (!sN) continue;
|
||||
if (sN.type === NT_INPUT) srcDesc = 'Input#' + sN.id;
|
||||
else srcDesc = 'Comb#' + (sN.combId !== undefined ? sN.combId : (sN.id & 0xFF));
|
||||
if (nd.type === NT_SR) {
|
||||
if (sSrc && srcDesc === sSrc) lk.dstPin = 0;
|
||||
else if (rSrc && srcDesc === rSrc) lk.dstPin = 1;
|
||||
} else {
|
||||
if (sSrc && srcDesc === sSrc) lk.dstPin = 1;
|
||||
else if (rSrc && srcDesc === rSrc) lk.dstPin = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g._source = sourceName;
|
||||
logicGraphs.push(g);
|
||||
}
|
||||
|
|
@ -821,13 +892,14 @@ var PlcConfigPage = (function() {
|
|||
var sn = nodes[l.srcIdx], dn = nodes[l.dstIdx];
|
||||
var srcIsIO = (sn.type === NT_OUTPUT || sn.type === NT_INPUT);
|
||||
var dstIsIO = (dn.type === NT_OUTPUT || dn.type === NT_INPUT);
|
||||
var dstIsSRRS = (dn.type === NT_SR || dn.type === NT_RS);
|
||||
var sW = srcIsIO ? IO_W : GATE_W, dW = dstIsIO ? IO_W : GATE_W;
|
||||
var dH = dstIsIO ? IO_H : GATE_H;
|
||||
var dH = dstIsIO ? IO_H : (dstIsSRRS ? 86 : GATE_H);
|
||||
var x1 = sn._lx + sW/2;
|
||||
var y1 = sn._ly;
|
||||
var dstInP = getNodeInPins(dn);
|
||||
var effPinIdx = (l.dstPin !== undefined) ? l.dstPin : 0;
|
||||
var y2 = dn._ly - dH/2 + dH * (effPinIdx + 1) / (dstInP + 1);
|
||||
var y2 = dn._ly - dH/2 + dH * (effPinIdx + 1) / (dstIsSRRS ? 3 : (dstInP + 1));
|
||||
var x2 = dn._lx - dW/2;
|
||||
var isActive = wiring && wiring.fromIdx === l.srcIdx;
|
||||
var isLinkSelected = (selectedLinkIdx === i);
|
||||
|
|
@ -1001,32 +1073,50 @@ var PlcConfigPage = (function() {
|
|||
// 中部:名称 + 序号
|
||||
ctx.fillStyle = '#e8e8e8';
|
||||
ctx.font = 'bold 11px -apple-system, sans-serif';
|
||||
ctx.fillText(info.icon + ' #' + gateIdx, x, y - 4);
|
||||
var titleText = info.icon + ' #' + gateIdx;
|
||||
if (n.type === NT_62P || n.type === NT_62D) {
|
||||
titleText += ' (' + (n.delayMs || 1000) + 'ms)';
|
||||
}
|
||||
ctx.fillText(titleText, x, y - 6);
|
||||
|
||||
// 连接数
|
||||
// 连接数 / 延时信息
|
||||
ctx.fillStyle = '#999';
|
||||
ctx.font = '9px monospace';
|
||||
ctx.fillText(realIn + '入 ' + realOut + '出', x, y + 14);
|
||||
if (n.type === NT_SR || n.type === NT_RS) {
|
||||
ctx.fillText(realIn + '入 ' + realOut + '出', x, y + 14);
|
||||
} else {
|
||||
ctx.fillText(realIn + '入 ' + realOut + '出', x, y + 14);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 输入引脚 ---
|
||||
if (inP > 0) {
|
||||
var renderPins = isIO ? inP : 1;
|
||||
var isSRRS = (n.type === NT_SR || n.type === NT_RS);
|
||||
var renderPins = isSRRS ? 2 : (isIO ? inP : 1);
|
||||
for (var p = 0; p < renderPins; p++) {
|
||||
var py = y - nh/2 + nh * (p + 1) / (inP + 1);
|
||||
var py = y - nh/2 + nh * (p + 1) / (isSRRS ? 3 : (inP + 1));
|
||||
// 外圈
|
||||
ctx.fillStyle = '#2e2e4a';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x - halfW, py, 7, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = '#4CAF50';
|
||||
ctx.strokeStyle = isSRRS ? (p === 0 ? '#FFC107' : '#FF5722') : '#4CAF50';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
// 内芯
|
||||
ctx.fillStyle = '#4CAF50';
|
||||
ctx.fillStyle = isSRRS ? (p === 0 ? '#FFC107' : '#FF5722') : '#4CAF50';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x - halfW, py, 3.5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
// SR/RS 引脚标签
|
||||
if (isSRRS) {
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.font = 'bold 10px monospace';
|
||||
ctx.textAlign = 'right';
|
||||
ctx.textBaseline = 'middle';
|
||||
var srl = (n.type === NT_SR) ? ['S', 'R'] : ['R', 'S'];
|
||||
ctx.fillText(srl[p], x - halfW - 12, py);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1133,6 +1223,12 @@ var PlcConfigPage = (function() {
|
|||
if (n.type !== NT_INPUT && n.type !== NT_OUTPUT) {
|
||||
newNode.inPins = (n.inPins !== undefined) ? n.inPins : ((NODE_INFO[n.type] || {}).inPins || 2);
|
||||
}
|
||||
// 延时字段
|
||||
if (n.delayMs !== undefined) newNode.delayMs = n.delayMs;
|
||||
// Comb id 和 RS 信息
|
||||
if (n.combId !== undefined) newNode.combId = n.combId;
|
||||
if (n.rsR) newNode.rsR = n.rsR;
|
||||
if (n.rsS) newNode.rsS = n.rsS;
|
||||
editNodes.push(newNode);
|
||||
}
|
||||
// 复制链路(按节点在新数组中的索引)
|
||||
|
|
@ -1140,7 +1236,11 @@ var PlcConfigPage = (function() {
|
|||
var l = g.links[j];
|
||||
var si = findEditNodeIndex(l.src_type, l.src_id);
|
||||
var di = findEditNodeIndex(l.dst_type, l.dst_id);
|
||||
if (si >= 0 && di >= 0) editLinks.push({ srcIdx: si, dstIdx: di });
|
||||
if (si >= 0 && di >= 0) {
|
||||
var el = { srcIdx: si, dstIdx: di };
|
||||
if (l.dstPin !== undefined) el.dstPin = l.dstPin;
|
||||
editLinks.push(el);
|
||||
}
|
||||
}
|
||||
editLoadedFromFile = true;
|
||||
|
||||
|
|
@ -1262,17 +1362,14 @@ var PlcConfigPage = (function() {
|
|||
var node = editNodes[idx];
|
||||
if (node.type === NT_INPUT || node.type === NT_OUTPUT) {
|
||||
showRebindSignalModal(idx);
|
||||
} else if (node.type === NT_AND || node.type === NT_OR) {
|
||||
} else if (node.type === NT_AND || node.type === NT_OR || node.type === NT_NOT) {
|
||||
showGateConfigModal(idx);
|
||||
} else if (node.type === NT_62P || node.type === NT_62D) {
|
||||
showDelayEditModal(idx);
|
||||
} else if (node.type === NT_SR || node.type === NT_RS) {
|
||||
showRSInfoModal(idx);
|
||||
} else {
|
||||
// 非门、62p、62d、上升沿、下降沿、SR、RS — 也可配置
|
||||
if (node.type === NT_NOT || node.type === NT_62P || node.type === NT_62D ||
|
||||
node.type === NT_RISING || node.type === NT_FALLING ||
|
||||
node.type === NT_SR || node.type === NT_RS) {
|
||||
showGateConfigModal(idx);
|
||||
} else {
|
||||
showToast(getNodeDisplayLabel(node) + ' (无需额外配置)', 'info');
|
||||
}
|
||||
showGateConfigModal(idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1283,12 +1380,81 @@ var PlcConfigPage = (function() {
|
|||
drawEditor();
|
||||
if (node.type === NT_INPUT || node.type === NT_OUTPUT) {
|
||||
showRebindSignalModal(idx);
|
||||
} else if (node.type === NT_62P || node.type === NT_62D) {
|
||||
showDelayEditModal(idx);
|
||||
} else if (node.type === NT_SR || node.type === NT_RS) {
|
||||
showRSInfoModal(idx);
|
||||
} else {
|
||||
// 运算模块:双击即选中,不再弹窗对齐显示信息在状态栏即可
|
||||
showToast(getNodeDisplayLabel(node), 'info');
|
||||
showGateConfigModal(idx);
|
||||
}
|
||||
}
|
||||
|
||||
/** 延时模块双击:弹出延时编辑框 */
|
||||
function showDelayEditModal(nodeIdx) {
|
||||
var node = editNodes[nodeIdx];
|
||||
var info = NODE_INFO[node.type] || {};
|
||||
var curMs = node.delayMs || 1000;
|
||||
var html = '<div class="modal-overlay" id="plc-modal"><div class="modal-box" style="max-width:380px">'
|
||||
+ '<h3>⏱️ ' + info.name + ' — 修改延时</h3>'
|
||||
+ '<p style="color:#999">当前延时: <strong>' + curMs + ' ms</strong></p>'
|
||||
+ '<input type="number" id="plc-delay-input" value="' + curMs + '" min="0" max="600000" step="100"'
|
||||
+ ' style="width:100%;padding:10px;border:1px solid #555;border-radius:6px;background:#2a2a3e;color:#fff;font-size:16px;margin:8px 0">'
|
||||
+ '<div style="margin-top:12px;display:flex;gap:8px">'
|
||||
+ '<button class="btn btn-primary" id="plc-modal-ok">💾 保存</button>'
|
||||
+ '<button class="btn btn-outline" id="plc-modal-cancel">取消</button>'
|
||||
+ '</div></div></div>';
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
document.body.appendChild(div.firstElementChild);
|
||||
|
||||
document.getElementById('plc-modal-cancel').onclick = closeModal;
|
||||
document.getElementById('plc-modal-ok').onclick = function() {
|
||||
var val = parseInt(document.getElementById('plc-delay-input').value);
|
||||
if (isNaN(val) || val < 0) { showToast('请输入有效的延时毫秒数', 'err'); return; }
|
||||
editNodes[nodeIdx].delayMs = val;
|
||||
closeModal();
|
||||
drawEditor();
|
||||
showToast('延时已更新: ' + val + ' ms', 'ok');
|
||||
};
|
||||
document.getElementById('plc-delay-input').onkeydown = function(e) {
|
||||
if (e.key === 'Enter') document.getElementById('plc-modal-ok').click();
|
||||
};
|
||||
setTimeout(function() {
|
||||
var inp = document.getElementById('plc-delay-input');
|
||||
if (inp) { inp.focus(); inp.select(); }
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/** SR/RS 双击:显示 R/S 连接信息 */
|
||||
function showRSInfoModal(nodeIdx) {
|
||||
var node = editNodes[nodeIdx];
|
||||
var info = NODE_INFO[node.type] || {};
|
||||
var sSrc = '', rSrc = '';
|
||||
for (var li = 0; li < editLinks.length; li++) {
|
||||
var l = editLinks[li];
|
||||
if (l.dstIdx === nodeIdx) {
|
||||
var srcNode = editNodes[l.srcIdx];
|
||||
var desc = getEditorNodeLabel(srcNode).replace(/\n/g, ' ');
|
||||
if (node.type === NT_SR) {
|
||||
if (l.dstPin === 0) sSrc = desc; else rSrc = desc;
|
||||
} else {
|
||||
if (l.dstPin === 1) sSrc = desc; else rSrc = desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
var html = '<div class="modal-overlay" id="plc-modal"><div class="modal-box" style="max-width:420px">'
|
||||
+ '<h3>🔒 ' + info.name + ' — 连接信息</h3>'
|
||||
+ '<p style="margin:8px 0"><strong>S (置位)</strong>: ' + (sSrc || '<span style="color:#f44336">未连接</span>') + '</p>'
|
||||
+ '<p style="margin:8px 0"><strong>R (复位)</strong>: ' + (rSrc || '<span style="color:#f44336">未连接</span>') + '</p>'
|
||||
+ '<p style="color:#999;font-size:12px;margin-top:12px">💡 拖动输出引脚到目标节点的 S/R 引脚来连线</p>'
|
||||
+ '<button class="btn btn-outline" id="plc-modal-cancel">关闭</button>'
|
||||
+ '</div></div>';
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
document.body.appendChild(div.firstElementChild);
|
||||
document.getElementById('plc-modal-cancel').onclick = closeModal;
|
||||
}
|
||||
|
||||
// 重新绑定 I/O 节点信号
|
||||
function showRebindSignalModal(nodeIdx) {
|
||||
var node = editNodes[nodeIdx];
|
||||
|
|
@ -1405,6 +1571,20 @@ var PlcConfigPage = (function() {
|
|||
return;
|
||||
}
|
||||
var link = { srcIdx: editWiring.fromIdx, dstIdx: pinHit.nodeIdx, dstPin: pinHit.pinIdx };
|
||||
// SR/RS 每个引脚只能接一根线
|
||||
var dstNode = editNodes[pinHit.nodeIdx];
|
||||
if (dstNode.type === NT_SR || dstNode.type === NT_RS) {
|
||||
for (var lc2 = 0; lc2 < editLinks.length; lc2++) {
|
||||
if (editLinks[lc2].dstIdx === pinHit.nodeIdx && editLinks[lc2].dstPin === pinHit.pinIdx) {
|
||||
var pinLabel = (dstNode.type === NT_SR) ? ['S','R'] : ['R','S'];
|
||||
showToast('引脚 ' + pinLabel[pinHit.pinIdx] + ' 已有连线,每个引脚只能接一根线', 'err');
|
||||
editWiring = null;
|
||||
editSelectedNode = -1;
|
||||
drawEditor();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
editLinks.push(link);
|
||||
showToast('连线已添加', 'ok');
|
||||
} else if (editWiring.fromSide === 'in' && pinHit.side === 'out') {
|
||||
|
|
@ -1564,8 +1744,9 @@ var PlcConfigPage = (function() {
|
|||
|
||||
function getNodeSize(node) {
|
||||
var isIO = (node.type === NT_OUTPUT || node.type === NT_INPUT);
|
||||
var isSRRS = (node.type === NT_SR || node.type === NT_RS);
|
||||
var inP = getNodeInPins(node);
|
||||
var h = isIO ? 80 : (66 + Math.max(0, inP - 2) * 14);
|
||||
var h = isIO ? 80 : (isSRRS ? 86 : (66 + Math.max(0, inP - 2) * 14));
|
||||
var w = isIO ? 250 : 170;
|
||||
return { w: w, h: h };
|
||||
}
|
||||
|
|
@ -1588,12 +1769,13 @@ var PlcConfigPage = (function() {
|
|||
var info = NODE_INFO[n.type] || NODE_INFO[0];
|
||||
var x = n._lx, y = n._ly;
|
||||
var isIO = (n.type === NT_INPUT || n.type === NT_OUTPUT);
|
||||
var isSRRS = (n.type === NT_SR || n.type === NT_RS);
|
||||
var inP = getNodeInPins(n);
|
||||
// 输入引脚(左侧)
|
||||
if (info.inPins > 0) {
|
||||
var testPins = isIO ? inP : 1;
|
||||
var testPins = isSRRS ? 2 : (isIO ? inP : 1);
|
||||
for (var p = 0; p < testPins; p++) {
|
||||
var py = y - sz.h/2 + sz.h * (p + 1) / (inP + 1);
|
||||
var py = y - sz.h/2 + sz.h * (p + 1) / (isSRRS ? 3 : (inP + 1));
|
||||
var dx = mx - (x - sz.w/2), dy = my - py;
|
||||
if (Math.sqrt(dx*dx + dy*dy) <= pinR) return { nodeIdx: i, side: 'in', pinIdx: p };
|
||||
}
|
||||
|
|
@ -1614,11 +1796,10 @@ var PlcConfigPage = (function() {
|
|||
var sn = editNodes[l.srcIdx], dn = editNodes[l.dstIdx];
|
||||
var szSrc = getNodeSize(sn), szDst = getNodeSize(dn);
|
||||
var x1 = sn._lx + szSrc.w/2, y1 = sn._ly;
|
||||
var dstInPins = getNodeInPins(dn);
|
||||
var gateInPins = getNodeInPins(dn);
|
||||
var effInPins = (dn.type !== NT_INPUT && dn.type !== NT_OUTPUT) ? 1 : gateInPins;
|
||||
var dstPinIdx2 = (l.dstPin !== undefined) ? l.dstPin : 0;
|
||||
var y2 = dn._ly - szDst.h/2 + szDst.h * (dstPinIdx2 + 1) / (effInPins + 1);
|
||||
var dstIsSRRS = (dn.type === NT_SR || dn.type === NT_RS);
|
||||
var gateInPins = getNodeInPins(dn);
|
||||
var dstPinIdx2 = (l.dstPin !== undefined) ? l.dstPin : 0;
|
||||
var y2 = dn._ly - szDst.h/2 + szDst.h * (dstPinIdx2 + 1) / (dstIsSRRS ? 3 : (gateInPins + 1));
|
||||
var x2 = dn._lx - szDst.w/2;
|
||||
var t = ((mx - x1)*(x2 - x1) + (my - y1)*(y2 - y1)) / ((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
|
||||
t = Math.max(0, Math.min(1, t));
|
||||
|
|
@ -1784,36 +1965,111 @@ var PlcConfigPage = (function() {
|
|||
|
||||
var rows = document.querySelectorAll('#plc-modal .plc-modal-row');
|
||||
for (var j = 0; j < rows.length; j++) {
|
||||
rows[j].onclick = function() {
|
||||
rows[j].onclick = function(e) {
|
||||
e.stopPropagation();
|
||||
var gateType = parseInt(this.getAttribute('data-gate-type'));
|
||||
var info = NODE_INFO[gateType];
|
||||
|
||||
var gateY = 100 + editNodes.filter(function(n) {
|
||||
return n.type !== NT_INPUT && n.type !== NT_OUTPUT;
|
||||
}).length * 120;
|
||||
|
||||
// 每种门类型独立序号,从已有最大序号+1 开始
|
||||
var nextIdx = editNextGateIdx[gateType] || 0;
|
||||
var gateId = nextIdx;
|
||||
|
||||
// 延时模块:弹出自定义延时输入框
|
||||
if (gateType === NT_62P || gateType === NT_62D) {
|
||||
var delayMs = prompt('请输入延时毫秒数 (默认 1000ms):', '1000');
|
||||
if (!delayMs) { closeModal(); return; }
|
||||
gateId = (parseInt(delayMs) << 8) | nextIdx;
|
||||
closeModal();
|
||||
showDelayInputModal(gateType, nextIdx, info);
|
||||
return;
|
||||
}
|
||||
|
||||
editNodes.push({
|
||||
type: gateType, id: gateId, x: 0, y: 0,
|
||||
_lx: 0, _ly: gateY,
|
||||
saddr: '', desc: '', inf: ''
|
||||
});
|
||||
// 非延时模块:直接添加
|
||||
addGateNode(gateType, nextIdx, 0, info);
|
||||
editNextGateIdx[gateType] = nextIdx + 1;
|
||||
closeModal();
|
||||
drawEditor();
|
||||
showToast('已添加: ' + info.name + ' #' + nextIdx, 'ok');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** 弹出延时毫秒输入框 */
|
||||
function showDelayInputModal(gateType, nextIdx, info) {
|
||||
var html = '<div class="modal-overlay" id="plc-modal"><div class="modal-box" style="max-width:380px">'
|
||||
+ '<h3>⏱️ ' + info.name + ' — 延时配置</h3>'
|
||||
+ '<p style="color:#999;margin:8px 0">请输入延时时间(毫秒)</p>'
|
||||
+ '<input type="number" id="plc-delay-input" value="1000" min="0" max="600000" step="100"'
|
||||
+ ' style="width:100%;padding:10px;border:1px solid #555;border-radius:6px;background:#2a2a3e;color:#fff;font-size:16px;margin:8px 0">'
|
||||
+ '<div style="margin-top:12px;display:flex;gap:8px">'
|
||||
+ '<button class="btn btn-primary" id="plc-modal-ok">✅ 确认添加</button>'
|
||||
+ '<button class="btn btn-outline" id="plc-modal-cancel">取消</button>'
|
||||
+ '</div></div></div>';
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
document.body.appendChild(div.firstElementChild);
|
||||
|
||||
document.getElementById('plc-modal-cancel').onclick = closeModal;
|
||||
document.getElementById('plc-modal-ok').onclick = function() {
|
||||
var val = parseInt(document.getElementById('plc-delay-input').value);
|
||||
if (isNaN(val) || val < 0) { showToast('请输入有效的延时毫秒数', 'err'); return; }
|
||||
closeModal();
|
||||
addGateNode(gateType, nextIdx, val, info);
|
||||
editNextGateIdx[gateType] = nextIdx + 1;
|
||||
drawEditor();
|
||||
};
|
||||
// 回车确认
|
||||
document.getElementById('plc-delay-input').onkeydown = function(e) {
|
||||
if (e.key === 'Enter') document.getElementById('plc-modal-ok').click();
|
||||
};
|
||||
setTimeout(function() {
|
||||
var inp = document.getElementById('plc-delay-input');
|
||||
if (inp) { inp.focus(); inp.select(); }
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/** 添加门节点到编辑区,自动找空白位置 */
|
||||
function addGateNode(gateType, nextIdx, delayMs, info) {
|
||||
var pos = findEmptyPosition();
|
||||
var node = {
|
||||
type: gateType, id: nextIdx,
|
||||
x: 0, y: 0,
|
||||
_lx: pos.x, _ly: pos.y,
|
||||
saddr: '', desc: '', no: 0
|
||||
};
|
||||
if (delayMs > 0) node.delayMs = delayMs;
|
||||
editNodes.push(node);
|
||||
showToast('已添加: ' + (info ? info.name : NODE_INFO[gateType].name) + ' #' + nextIdx, 'ok');
|
||||
}
|
||||
|
||||
/** 寻找画布上最空白的区域 */
|
||||
function findEmptyPosition() {
|
||||
if (editNodes.length === 0) return { x: 0, y: 0 };
|
||||
// 先计算已有节点的包围盒,在右侧偏下寻找空白
|
||||
var minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
||||
for (var i = 0; i < editNodes.length; i++) {
|
||||
var n = editNodes[i];
|
||||
if (n._lx < minX) minX = n._lx;
|
||||
if (n._lx > maxX) maxX = n._lx;
|
||||
if (n._ly < minY) minY = n._ly;
|
||||
if (n._ly > maxY) maxY = n._ly;
|
||||
}
|
||||
// 在所有节点下方居中放置
|
||||
var candidates = [
|
||||
{ x: 0, y: maxY + 140 },
|
||||
{ x: 0, y: minY - 140 },
|
||||
{ x: maxX + 300, y: (minY + maxY) / 2 },
|
||||
{ x: minX - 300, y: (minY + maxY) / 2 }
|
||||
];
|
||||
for (var c = 0; c < candidates.length; c++) {
|
||||
if (!isPositionOccupied(candidates[c].x, candidates[c].y)) {
|
||||
return candidates[c];
|
||||
}
|
||||
}
|
||||
return { x: 0, y: maxY + 140 };
|
||||
}
|
||||
|
||||
function isPositionOccupied(x, y) {
|
||||
for (var i = 0; i < editNodes.length; i++) {
|
||||
var n = editNodes[i];
|
||||
if (Math.abs(n._lx - x) < 120 && Math.abs(n._ly - y) < 80) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function showModal(html, onSelect) {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
|
|
@ -1919,39 +2175,89 @@ var PlcConfigPage = (function() {
|
|||
}
|
||||
|
||||
function sigXml(n) { return '<Signal no="' + n.id + '" x="' + Math.round(n._lx) + '" y="' + Math.round(n._ly) + '" />'; }
|
||||
function gateTag(n) {
|
||||
function gateTag(n, nodeIdx) {
|
||||
var tn = typeNames[n.type] || '?';
|
||||
var a = ' type="' + tn + '" id="' + (n.id & 0xFF) + '" x="' + Math.round(n._lx) + '" y="' + Math.round(n._ly) + '"';
|
||||
if (n.inPins && n.inPins > 2) a += ' inPins="' + n.inPins + '"';
|
||||
if (n.type === NT_62P || n.type === NT_62D) a += ' delayMs="' + (n.id >> 8) + '"';
|
||||
if (n.type === NT_62P || n.type === NT_62D) a += ' delayMs="' + (n.delayMs || 1000) + '"';
|
||||
// SR/RS: 输出 R/S 属性
|
||||
if ((n.type === NT_SR || n.type === NT_RS) && nodeIdx !== undefined) {
|
||||
var srLinks = [];
|
||||
for (var li = 0; li < editLinks.length; li++) {
|
||||
if (editLinks[li].dstIdx === nodeIdx) srLinks.push(editLinks[li]);
|
||||
}
|
||||
// 按 dstPin 排序,未设置的用出现顺序兜底
|
||||
var hasPin = false;
|
||||
for (var sl = 0; sl < srLinks.length; sl++) {
|
||||
if (srLinks[sl].dstPin !== undefined) { hasPin = true; break; }
|
||||
}
|
||||
if (!hasPin) {
|
||||
// 兜底:SR 按出现顺序 S→R,RS 按出现顺序 R→S
|
||||
for (var sl2 = 0; sl2 < srLinks.length; sl2++) {
|
||||
if (n.type === NT_SR) srLinks[sl2].dstPin = sl2; // 0=S, 1=R
|
||||
else srLinks[sl2].dstPin = 1 - sl2; // 0=R, 1=S
|
||||
}
|
||||
}
|
||||
for (var sl3 = 0; sl3 < srLinks.length; sl3++) {
|
||||
var l3 = srLinks[sl3];
|
||||
var srcNode = editNodes[l3.srcIdx];
|
||||
var srcDesc = '';
|
||||
if (srcNode.type === NT_INPUT) {
|
||||
srcDesc = 'Input#' + srcNode.id;
|
||||
} else {
|
||||
srcDesc = 'Comb#' + (srcNode.combId !== undefined ? srcNode.combId : (srcNode.id & 0xFF));
|
||||
}
|
||||
if (n.type === NT_SR) {
|
||||
if (l3.dstPin === 0) a += ' S="' + srcDesc + '"';
|
||||
else a += ' R="' + srcDesc + '"';
|
||||
} else {
|
||||
if (l3.dstPin === 1) a += ' S="' + srcDesc + '"';
|
||||
else a += ' R="' + srcDesc + '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
return '<Gate' + a + ' />';
|
||||
}
|
||||
|
||||
/** 后序遍历分配 combId(自底向上,从 0 起) */
|
||||
function assignCombIds(idx, ctrObj, cGatesArr) {
|
||||
editNodes[idx].combId = undefined;
|
||||
for (var ui = 0; ui < adjIn[idx].length; ui++) {
|
||||
var up = adjIn[idx][ui];
|
||||
if (editNodes[up].type > 1 && cGatesArr.indexOf(up) >= 0) {
|
||||
assignCombIds(up, ctrObj, cGatesArr);
|
||||
}
|
||||
}
|
||||
editNodes[idx].combId = ctrObj.count++;
|
||||
}
|
||||
|
||||
function combXml(idx, indent) {
|
||||
var s = '';
|
||||
var cid = editNodes[idx].combId;
|
||||
s += indent + '<Comb id="' + (cid !== undefined ? cid : 0) + '">\n';
|
||||
var innerIndent = indent + ' ';
|
||||
var di = [];
|
||||
for (var fi = 0; fi < adjIn[idx].length; fi++)
|
||||
if (editNodes[adjIn[idx][fi]].type === NT_INPUT) di.push(adjIn[idx][fi]);
|
||||
if (di.length > 0) {
|
||||
s += indent + '<Inputs>\n';
|
||||
for (var d = 0; d < di.length; d++) s += indent + ' ' + sigXml(editNodes[di[d]]) + '\n';
|
||||
s += indent + '</Inputs>\n';
|
||||
s += innerIndent + '<Inputs>\n';
|
||||
for (var d = 0; d < di.length; d++) s += innerIndent + ' ' + sigXml(editNodes[di[d]]) + '\n';
|
||||
s += innerIndent + '</Inputs>\n';
|
||||
}
|
||||
for (var ui = 0; ui < adjIn[idx].length; ui++)
|
||||
if (editNodes[adjIn[idx][ui]].type > 1) {
|
||||
s += indent + '<Comb>\n';
|
||||
s += combXml(adjIn[idx][ui], indent + ' ');
|
||||
s += indent + '</Comb>\n';
|
||||
if (editNodes[adjIn[idx][ui]].type > 1 && cGates.indexOf(adjIn[idx][ui]) >= 0) {
|
||||
s += combXml(adjIn[idx][ui], innerIndent);
|
||||
}
|
||||
s += indent + gateTag(editNodes[idx]) + '\n';
|
||||
s += innerIndent + gateTag(editNodes[idx], idx) + '\n';
|
||||
var dos = [];
|
||||
for (var oi = 0; oi < adjOut[idx].length; oi++)
|
||||
if (editNodes[adjOut[idx][oi]].type === NT_OUTPUT) dos.push(adjOut[idx][oi]);
|
||||
if (dos.length > 0) {
|
||||
s += indent + '<Outputs>\n';
|
||||
for (var o = 0; o < dos.length; o++) s += indent + ' ' + sigXml(editNodes[dos[o]]) + '\n';
|
||||
s += indent + '</Outputs>\n';
|
||||
s += innerIndent + '<Outputs>\n';
|
||||
for (var o = 0; o < dos.length; o++) s += innerIndent + ' ' + sigXml(editNodes[dos[o]]) + '\n';
|
||||
s += innerIndent + '</Outputs>\n';
|
||||
}
|
||||
s += indent + '</Comb>\n';
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
@ -1977,7 +2283,7 @@ var PlcConfigPage = (function() {
|
|||
xml += ' <Inputs>\n';
|
||||
for (var ii = 0; ii < cIns.length; ii++) xml += ' ' + sigXml(editNodes[cIns[ii]]) + '\n';
|
||||
xml += ' </Inputs>\n';
|
||||
xml += ' ' + gateTag(editNodes[cGates[0]]) + '\n';
|
||||
xml += ' ' + gateTag(editNodes[cGates[0]], cGates[0]) + '\n';
|
||||
xml += ' <Outputs>\n';
|
||||
for (var oo = 0; oo < cOuts.length; oo++) xml += ' ' + sigXml(editNodes[cOuts[oo]]) + '\n';
|
||||
xml += ' </Outputs>\n';
|
||||
|
|
@ -2022,14 +2328,32 @@ var PlcConfigPage = (function() {
|
|||
if (!isDownstream) combRoots.push(combRootArr[rr]);
|
||||
}
|
||||
// 输出 Comb(每个 combRoot 对应一个顶层 Comb,内部递归嵌套)
|
||||
// 先分配 combId
|
||||
var combIdCtr = { count: 0 };
|
||||
for (var tt = 0; tt < combRoots.length; tt++) {
|
||||
xml += ' <Comb>\n';
|
||||
xml += combXml(combRoots[tt], ' ');
|
||||
xml += ' </Comb>\n';
|
||||
assignCombIds(combRoots[tt], combIdCtr, cGates);
|
||||
}
|
||||
// 输出链级门
|
||||
for (var tt = 0; tt < combRoots.length; tt++) {
|
||||
xml += combXml(combRoots[tt], ' ');
|
||||
}
|
||||
// 输出链级门 — 若存在直接 Input 连接(未进入 Comb 的),先行输出
|
||||
for (var cg = 0; cg < chainGates.length; cg++) {
|
||||
xml += ' ' + gateTag(editNodes[chainGates[cg]]) + '\n';
|
||||
var cgNode = editNodes[chainGates[cg]];
|
||||
var directInputs = [];
|
||||
for (var cui = 0; cui < adjIn[chainGates[cg]].length; cui++) {
|
||||
var upIdx = adjIn[chainGates[cg]][cui];
|
||||
if (editNodes[upIdx].type === NT_INPUT) {
|
||||
directInputs.push(upIdx);
|
||||
}
|
||||
}
|
||||
if (directInputs.length > 0) {
|
||||
xml += ' <Inputs>\n';
|
||||
for (var di2 = 0; di2 < directInputs.length; di2++) {
|
||||
xml += ' ' + sigXml(editNodes[directInputs[di2]]) + '\n';
|
||||
}
|
||||
xml += ' </Inputs>\n';
|
||||
}
|
||||
xml += ' ' + gateTag(cgNode, chainGates[cg]) + '\n';
|
||||
}
|
||||
// 链级 Outputs — 仅包含直接连到 chainGate 的输出信号
|
||||
// (Comb 内部的输出信号已在 combXml 中处理)
|
||||
|
|
|
|||
Loading…
Reference in New Issue