# -*- coding: utf-8 -*-
"""
DB_MbMultiRead 与 FB_MbMultiRead 接口一致性校验
==============================================
目的：DB 只是"容器"，它必须和 FB 的三个 VAR_IN_OUT 引脚严格对得上，
否则用户在 OB1 里连线时会出现区域长度错误 / 类型不匹配。

检查项（全部基于权威源文件，不凭记忆写）：
  1. DB 里三个成员的类型、上下限，与 FB 三个 IN_OUT 引脚逐项一致
  2. DB 引用的两个 UDT 名字，在 FB 源文件里确实有 TYPE 定义
  3. 两个 UDT 的字段名与 FB 源里定义的一致（防止改了 UDT 忘了改 DB）
  4. DB 的 optimized 访问设置与 FB 文档里的要求一致
  5. DB 长度/字段与数组边界（0..999 共 1000 个字）

用法：python verify_db_multiread.py
"""
import os
import re
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
DB = os.path.join(HERE, "DB_MbMultiRead.scl")
FB = os.path.join(HERE, "..", "scl", "FB_MbMultiRead.scl")

fails = []
checks = 0


def ck(cond, msg):
    global checks
    checks += 1
    if cond:
        print("  [OK] %s" % msg)
    else:
        print("  [FAIL] %s" % msg)
        fails.append(msg)


def read(p):
    with open(p, "r", encoding="utf-8-sig") as f:
        return f.read()


db = read(DB)
fb = read(FB)

print("=" * 66)
print("  DB_MbMultiRead  ×  FB_MbMultiRead  接口一致性校验")
print("=" * 66)

# ---------- 1. DB 三个成员 ----------
print("\n[1] DB 成员与 FB 的三个 VAR_IN_OUT 引脚对齐")
m_sta = re.search(r'Station\s*(\{[^}]*\})?\s*:\s*Array\[(\d+)\.\.(\d+)\]\s*of\s+"(\w+)"', db)
m_task = re.search(r'Task\s*(\{[^}]*\})?\s*:\s*Array\[(\d+)\.\.(\d+)\]\s*of\s+"(\w+)"', db)
m_buf = re.search(r'Buffer\s*(\{[^}]*\})?\s*:\s*Array\[(\d+)\.\.(\d+)\]\s*of\s*(\w+)', db)

ck(m_sta is not None, "DB 里有 Station 成员")
ck(m_task is not None, "DB 里有 Task 成员")
ck(m_buf is not None, "DB 里有 Buffer 成员")

# FB 的 IN_OUT 声明
fb_sta = re.search(r'aSta\s*:\s*ARRAY\[(\d+)\.\.(\d+)\]\s*OF\s+"(\w+)"', fb)
fb_task = re.search(r'aTask\s*:\s*ARRAY\[(\d+)\.\.(\d+)\]\s*OF\s+"(\w+)"', fb)
fb_buf = re.search(r'aBuf\s*:\s*ARRAY\[(\d+)\.\.(\d+)\]\s*OF\s*(\w+)', fb)
ck(fb_sta and fb_task and fb_buf, "FB 里三个 IN_OUT 引脚都在（aSta / aTask / aBuf）")

if m_sta and fb_sta:
    ck((m_sta.group(2), m_sta.group(3), m_sta.group(4))
       == (fb_sta.group(1), fb_sta.group(2), fb_sta.group(3)),
       "Station 维度/类型 == aSta：Array[%s..%s] of %s"
       % (m_sta.group(2), m_sta.group(3), m_sta.group(4)))
if m_task and fb_task:
    ck((m_task.group(2), m_task.group(3), m_task.group(4))
       == (fb_task.group(1), fb_task.group(2), fb_task.group(3)),
       "Task 维度/类型 == aTask：Array[%s..%s] of %s"
       % (m_task.group(2), m_task.group(3), m_task.group(4)))
if m_buf and fb_buf:
    ck((m_buf.group(2), m_buf.group(3), m_buf.group(4).lower())
       == (fb_buf.group(1), fb_buf.group(2), fb_buf.group(3).lower()),
       "Buffer 维度/类型 == aBuf：Array[%s..%s] of %s"
       % (m_buf.group(2), m_buf.group(3), m_buf.group(4)))

# ---------- 2. UDT 存在性 ----------
print("\n[2] DB 引用的 UDT 在 FB 源文件里有定义")
for name in (m_sta.group(4), m_task.group(4)):
    ck(re.search(r'TYPE\s+"%s"' % re.escape(name), fb) is not None,
       'UDT "%s" 在 FB 源文件中有 TYPE 定义' % name)

# ---------- 3. UDT 字段一致 ----------
print("\n[3] UDT 字段与 FB 源里的定义一致")
for name, expect in (("UDT_MbStation", ["sIP", "uPort", "uUnitId"]),
                      ("UDT_MbTask", ["uStaNo", "bWrite", "uFunc", "uAddr",
                                      "uCount", "uBufOff", "bEnable", "uTrig"])):
    body = re.search(r'TYPE\s+"%s".*?END_TYPE' % re.escape(name), fb, re.S)
    ck(body is not None, "%s 定义段找到" % name)
    if not body:
        continue
    for fld in expect:
        ck(re.search(r'\b%s\s*:' % re.escape(fld), body.group(0)) is not None,
           "%s 含字段 %s" % (name, fld))

# ---------- 4. 优化访问 ----------
print("\n[4] 访问方式")
ck("S7_Optimized_Access := 'TRUE'" in db, "DB 自身是优化访问（S7-1500 全局 DB 常规做法）")
ck(re.search(r'VERSION\s*:\s*0\.\d+', db) is not None, "DB 声明了 VERSION")

# ---------- 5. 长度 ----------
print("\n[5] 边界")
if m_buf:
    lo, hi = int(m_buf.group(2)), int(m_buf.group(3))
    ck(hi - lo + 1 == 1000, "Buffer 共 %d 个字（FB 文档说的是 1000）" % (hi - lo + 1))
if m_sta:
    ck((int(m_sta.group(2)), int(m_sta.group(3))) == (1, 8), "Station 共 8 个槽位（1..8）")
if m_task:
    ck((int(m_task.group(2)), int(m_task.group(3))) == (1, 32), "Task 共 32 条（1..32）")

print("\n" + "=" * 66)
print("  结果：%d 项检查，%d 项失败" % (checks, len(fails)))
if fails:
    for f in fails:
        print("   - %s" % f)
    sys.exit(1)
print("  全部通过 —— DB 与 FB 接口自洽")
