---
# interlock 实现根据信号状态触发输出，可以用于报警或联锁
# 可以在本配置中定义多组联锁
# 每一组联锁由以下几个指令组成：
# - comment: 该组联锁的注释
# - name: 该组联锁的名称
# - DB: 该组联锁使用的DB块
# - data: 该组联锁的中转数据，每一项数据可以自动上传到HMI，即有S7_m_c属性
# - input: 触发该组联锁的输入列表，每一项输入都可以定义触发条件
# - output: 该组联锁的输出列表

# 转换程序会自动构造对应的DB块，大致内部结构为:
#     DB.enable           允许联锁，默认为true，可省略，该项具有S7_m_c属性
#
#     DB.<data_1>         名称为第1组联锁 data 的对应项 name，该项具有S7_m_c属性
#     DB.<data_2>         名称为第1组联锁 data 的对应项 name，该项具有S7_m_c属性
#     ......
#     DB.<data_n>         名称为第1组联锁 data 的对应项 name，该项具有S7_m_c属性
#
#     以下用于跟踪状态（用于类型为 rising falling change 的输入项）
#     DB.b_1_fo           内部数据用于第1组联锁跟踪边沿
#     ......
#     DB.b_n_fo           内部数据用于第1组联锁跟踪边沿，数量与边沿触发项对应
#
#     如果多组联锁共用该DB块，会在这里存储第2组以后联锁的数据结构
# 
# 可1组联锁使用1个DB块，也可以多组联锁共用1个DB块
# 共用情况下，enable项只有一个
# 注意：由上面的DB结构可知，同一DB所在的联锁中
# data input reset output 四部分的每一小项的 name 值不能重复
# 否则会造成DB字段重复，会报错。

#CPU: AS1           # 指示属于哪一个CPU
#feature: interlock # 指示本配置为生成联锁代码
#feature: IL        # IL 和 interlock 等效
name: AS1-interlock # 相当于上面2条被注释的指令

includes: ~

symbols:
# 系统已有1个内置符号，其内容由本程序自动生成:
# - [Interlock_Loop, FC518]   主调用函数，在OB中加入对它的调用。
# 可以在对上述内置符号的地址进行更改，只要重新定义就可以了，注意保持名称一致

# 以下为自定义符号
- [DI02-10, I5.1]
- [DI02-11, I+]
- [DI02-13, I5.4]
- [DI02-15, I5.6]
- [DI02-16, I5.7]
- [DI02-17, I6.0]
- [DI02-18, I6.1]
- [DI02-19, I6.2]
- [DI02-20, I6.3]
- [DO04-01, Q6.0]
- [DO04-03, Q6.2]
- [DO04-04, Q6.3]

list:

# === 例1 一个简单的联锁
- comment: 声光报警
  DB: [IL_ESDBTN, DB121, ~, 声光报警联锁]

  data:
  - test    # 用于HMI测试，建议提供该项
  - reset   # 用于HMI重置，建议提供该项
  - output  # 用于HMI读联锁输出，没有其它output时建议提供该项
  - {name: EBTN, read: DI02-17, comment: 人工报警按钮}   # 实现从 DI02-17 读值
  - {name: GIA001, read: DI02-18, comment: 可燃气报警}   # 实现从 DI02-17 读值
  - {name: SL, write: DO04-01, comment: 声光报警DO}      # 实现输出值到 DO04-01
  # 可以在 input reset output 区直接使用上方定义的数据
  # 引用数据项的名称就可以了，见下方示例：
  input:   # 以下信号的上升沿会触发联锁
  - test
  - EBTN
  - GIA001
  reset:   # 以下输入信号会重置联锁
  - reset
  output:  # 以下为联锁动作后的输出
  - output
  - SL

# === 例2 详细用例
- comment: 外输泵房间联锁  # 联锁说明，字符串，可省略

  ############################################################
  # DB块定义
  # 可以一组联锁使用1个DB块，也可以多组联锁共用1个DB块
  # 类型: S7符号定义 | S7符号引用
  DB: [IL_pump, DB122]
  ############################################################

  ############################################################
  # 运行时设置允许报警连锁，可省略，省略时为由 $enable 决定。
  # 类型:  S7符号定义 | S7符号引用 | S7表达式
  # 值类型: boolean
  enable: '"IL_ESDBTN".enable'
  ############################################################

  ############################################################
  # 中转数据列表，每一项的值是一个布尔值。
  # 
  # 1 中转数据项最大的意义在于它对 PLC 和 HMI 是可见的，
  #   该项中转数据一定会存储在 DB 块中，
  #   而 input reset output 的数据在 DB 块中并不保证存在。
  #   系统会自动在每一中转数据项对应的 DB 字段加上 S7_m_c 属性。
  # 2 中转项还可以自定义数据来源和数据目标，实现数值的传递。
  # 3 中转项还可以对读取数据进行计算。
  # 
  # 通常实践中是基于前2个特性来用不用中转数据项的。
  # 第3个特性可以实现的例子：
  #   根据联锁动作和其它条件判断后决定是否去关阀关电机。
  # 
  # 注意，中转数据本身并不意味着一定参与联锁运算，但实际使用中一般与联锁相关。
  data:
  # 每一输入项类型为 data_item 对象，包含以下属性:
  #   name    暴露给 OS 的变量名称，字符串
  #           必填项，
  #   read    本项读取的过程值来源，可省略
  #           类型:  S7符号定义 | S7符号引用 | S7表达式
  #   write   本项写入至过程值目标，可省略
  #           类型:  S7符号定义 | S7符号引用 (不能是S7表达式)
  #   type    本项的 S7 类型，可省略，默认为 BOOL
  #   init    本项的初始值，可省略
  #   comment 该项的注释，可省略
  - comment: 启动声光报警器输出
    name: output
    write: DO04-03
  - name: test                 # 仅有 name 的中转项
  - reset                      # 只有 name 的简写方式，它等效于 `- {name: reset}`
  - comment: 外输泵停止状态
    name: stop
    read: NOT "DI02-15"        # 用表达式给该项赋值
  - comment: 温度高             # 完整写法的中转数据项
    name: temperature          # 自定义在DB中的名称
    read: '"pump485".TIT-1201' # 读值为S7表达式 这里读485通讯块中的温度
    write: '"WCS".temperature' # 写值为S7表达式 这里转存温度到水冷系统DB块中
    type: INT                  # S7 类型为 INT
    init: 10                   # 初始值为 10
    # 同时有 read 和 write 的项更多用于数值的运算后传递。
  - comment: 外输泵润滑系统工作状态
    name: lubrication_work
    read: [DI02-09, I5.0]      # 读值为S7符号的项
  - {name: PowerReady, read: DI02-12, comment: 高压到位}
  ############################################################

  ############################################################
  # 输入列表，每一项代表一个信号，最终值为布尔值
  input:
  # 每一输入项可以包含以下属性:
  #   value   本项的值读取自过程值，必填项
  #           类型为: data项名称 | S7符号定义 | S7符号引用 | SCL表达式
  #           注意，在SCL表达式内部中，不能直接出现data项名称、S7符号定义、S7符号引用
  #   trigger 指示本输入项的触发类型，通常省略，默认为 rising
  #           以下为可用值：
  #           rising     上升沿
  #           falling    下降沿
  #           change     变化边沿
  #           on         信号接通
  #           off        信号断开
  #   comment 该项的注释，可省略
  #   and     指示 value 是由多个项组成，并取与运算结果做为本项的 value 值
  #           具体见例3的说明

  # 通常上完整形式过于复杂，日常使用时的情形通常比较简单，可以使用简化形式
  # 即只使用输入项的 value 属性，整个输入项为简化为只书写 value 值
  - test                       # data项名称作为输入项，与 `{value: test}` 等效
  - DI02-11                    # 符号引用作为输入项，与 `{value: DI02-11}` 等效
  - [DI02-14, I5.5]            # 符号定义作为输入项，与 `{value: [DI02-14, I5.5]}` 等效
  - '"PIT-1201".AH_Flag'       # SCL表达式作为该输入项，与 `{value: '"PIT-1201".AH_Flag'}` 等效
  - IL_pump.temperature > 100  # SCL表达式作为输入项，与 `{value: IL_pump.temperature > 100}` 等效
  # 编译器会自动转换为完整的输入项对象，并应用默认的 rising 触发类型。

  # 下方是一些完整形式的输入项
  - comment: 外输泵润滑系统
    trigger: falling
    value: lubrication_work                       # 触发过程值是一个 data 项名称
  - comment: 外输泵错误
    value: DI02-10                                # 触发过程值是一个S7符号
  - comment: 外输泵冷却水工作正常
    # 触发类型为接通，保证电机运行时总是联锁
    trigger: on
    # 触发过程值是一个SCL表达式
    # 注意，在表达式中不可以直接写 data 中转项名称
    # 必须加DB前缀的形成完整SCL地址，比如下方的 WCS_work：
    value: '"IL_pump".WCS_work XOR MOT0101.run'   # 触发过程值是一个SCL表达式
  - comment: 外输泵高压失电
    # 触发类型为断开，保证无高压时联锁报警
    trigger: off
    value: 'PowerReady'
  - and:               # 来油罐液位低
    - LIT0205A.AL_flag # 1#罐液位低
    - LIT0205B.AL_flag # 2#罐液位低
  ############################################################

  ############################################################
  # 复位列表，每一项为一个复位，它代表最终的值是一个布尔值
  # 有任何一项为 true ，就会将本联锁的所有输出复位。
  reset:
  # 每一个复位关联项类型为:
  #   data项名称 | S7符号定义 | S7符号引用 | SCL表达式
  # 注意，在SCL表达式内部中，不能直接出现data项名称、S7符号定义、S7符号引用
  # 输出关联项的值都是布尔值
  - reset              # data项名称 这里用来人工确认
  - DI02-13            # S7符号引用
  - Alarm_SL.reset     # SCL表达式
  ############################################################

  ############################################################
  # 联锁输出
  # 联锁输出值是一个BOOL项，由所有输入项的触发条件 OR 运算而得。
  # 任何一 reset 项会复位输出
  # 系统内置了 DB.output 字段，具有 S7_m_c 属性，可以被HMI读取，
  output:

  # 每一输出关联项可以包含以下属性:
  #   value      本项的值输出的过程值，必填项
  #              类型为: data项名称 | S7符号定义 | S7符号引用 | SCL表达式
  #              注意，在SCL表达式内部中，不能直接出现data项名称、S7符号定义、S7符号引用
  #   inversion  是否反相输出，boolean字面量，可省略，默认为false
  #   comment    该项的注释，可省略
  #   reset      本输出项的单独复位条件，可省略
  #              类型为: data项名称 | S7符号定义 | S7符号引用 | SCL表达式
  #              本项只针对本输出复位，而上一级rest项是复位所有输出项
  #              表达式中可以引用一个特殊的变量 `inputs` 表示至少有一个 input 信号触发
  #              注意只有边沿信号触发时，只存在一个周期。
  #              这样可以用 'NOT inputs' 表示所有 input 信号都断开后自动复位
  #              其它值在为 true 时自动复位
  #              单独复位的用处在于可以提供独立于总复位的联锁动作输出和联锁提醒输出
  #              单独复位可以避免在总复位信号到来之前，必须长时间输出
  # 输出关联项的值都是布尔值，联锁程序会保证关联过程值与联锁输出值相同。
  # output值必须指向一个可赋值的S7地址，比如：
  #   正确: DO04-02                 错误: DI02-12
  #   正确: AFan_1204A.start_CMD    错误: 'NOT AFan_1204A.run'
  # 转换器无法查验输出项的合理性
  # 如果没有任何输出项，转换程序不生成任何关联输出。这时可以用 DB.output 来自行编程或HMI处理

  # 通常上面定义的完整形式过于复杂，日常使用时的情形通常比较简单，可以使用简化形式
  # 即只使用输出项的 value 属性，整个输出项简化为只书写 value 的值
  # 编译器会自动转换为完整的输出项对象，默认不反相，不单独复位。
  - output                 # 这是 data 项，也可以用S7符号或表达式

  # 输出项为一个对象的写法
  - value: [DO04-02, Q6.1] # 输出到S7符号引用，代表停泵动作线圈
    reset: stop            # 停泵信号单独复位
                           # 这里的值是data项，还可以是符号和表达式。
  - value: IL_ESDBTN.test
    reset: NOT inputs      # 当所有输入信号消失时，自动复位
    comment: 触发上级报警
  ############################################################

# === 例3
# 以下展示2组联锁共用一个DB块的示例
# 广播系统的启动和停止是2组联锁
# 它们共用一个 DB ，共享 enable 项和 data 项
# 注意： 由于共用，2组联锁的 data input reset output 项名称都不能重复
# 同时这里也展示上方没展示的指令用法：
# - 输入项的 and 属性
# - 输出项的 inversion 属性
# - 输出项的 default 属性
# - extra_code 指令

- DB: [IL_sound, DB123, ~, 事件播报]
  # boolean 字面量，值只能为true或false
  # 初始时是否允许报警连锁，可省略，默认为 true。
  # 非 false 值的其它输入会都视为 true
  $enable: false  # 共用DB时， $enable enable 指令只能在其中一个联锁上设置，不允许重复设置
  data:
  - {name: play, write: DO04-04, comment: 广播设备播放}
  - {name: stop, write: [DO04-06, Q6.5],  comment: 广播设备停止}
  - {name: WCounter_signal, read: DI02-16, comment: 计件器检测}
  - {name: comfirm, read: DI02-20, comment: 人工确认}
  - {name: RSTSWT, read: DI02-19, comment: 人工停播旋钮}
  input:
  - WCounter_signal           # 计件器检测
  # input项除了上方介绍的 value trigger comment 属性外
  # 还有一个高级属性 and ，它指示 raad 属性是由多个项组成的数组
  # 当 and 列表被设置时，value 值是由多个 and 项进行与运算形成
  - and:                      # 每一项的类型为: data项名称 | S7符号定义 | S7符号引用 | SCL表达式
    - comfirm                 # 人工确认
    - '"WCounter".fix'        # 检测异常
    trigger: on
  output:
  - play                      # 广播设备播放
    
  # 自定义代码，用于编写与该联锁相关的复杂自定义功能
  # 这里的代码会加入到当前联锁逻辑之后
  extra_code: |-
    // 记录播报次数
    IF IL_sound.output AND NOT "WCounter".follower THEN
        "WCounter".count := "WCounter".count + 1;
    ENDIF;
    "WCounter".follower := IL_sound.output;

- DB: IL_sound
  input:
  - RSTSWT
  output:
  - value: stop
    # 反相输出，代表断开时广播停止
    # 注意，反相时如果非使能，将输出为 true
    inversion: TRUE
    # 当 enable 为 false 时，输出项的默认值
    # 通常用于反相时，非使能时输出 false
    default: FALSE


# 用户自写SCL代码，放在循环的开始和结束处。
loop_begin: |-
  // 这里的代码将在 Interlock_Loop 循环开始时执行
loop_end: |-
  // 这里的代码将在 Interlock_Loop 循环结束时执行

options : # 选项，非必需，无需要时可以全部删除或注释掉。
  # output_file : 'example.scl'

---
# 内容与 AS1-interlock 相同，仅仅是CPU中的平台不一样。
name: AS2-interlock

includes: ~

symbols:
# 系统已有1个内置符号，其内容由本程序自动生成:
# - [Interlock_Loop, FC518]   主调用函数，在OB中加入对它的调用。
# 可以在对上述内置符号的地址进行更改，只要重新定义就可以了，注意保持名称一致

# 以下为自定义符号
- [DI02-10, I5.1]
- [DI02-11, I+]
- [DI02-13, I5.4]
- [DI02-15, I5.6]
- [DI02-16, I5.7]
- [DI02-17, I6.0]
- [DI02-18, I6.1]
- [DI02-19, I6.2]
- [DI02-20, I6.3]
- [DO04-01, Q6.0]
- [DO04-03, Q6.2]
- [DO04-04, Q6.3]

list:

# === 例1 一个简单的联锁
- comment: 声光报警
  DB: [IL_ESDBTN, DB121, ~, 声光报警联锁]

  data:
  - test    # 用于HMI测试，建议提供该项
  - reset   # 用于HMI重置，建议提供该项
  - output  # 用于HMI读联锁输出，没有其它output时建议提供该项
  - {name: EBTN, read: DI02-17, comment: 人工报警按钮}   # 实现从 DI02-17 读值
  - {name: GIA001, read: DI02-18, comment: 可燃气报警}   # 实现从 DI02-17 读值
  - {name: SL, write: DO04-01, comment: 声光报警DO}      # 实现输出值到 DO04-01
  # 可以在 input reset output 区直接使用上方定义的数据
  # 引用数据项的名称就可以了，见下方示例：
  input:   # 以下信号的上升沿会触发联锁
  - test
  - EBTN
  - GIA001
  reset:   # 以下输入信号会重置联锁
  - reset
  output:  # 以下为联锁动作后的输出
  - output
  - SL

# === 例2 详细用例
- comment: 外输泵房间联锁  # 联锁说明，字符串，可省略

  DB: [IL_pump, DB122]
  enable: '"IL_ESDBTN".enable'
  data:
  - comment: 启动声光报警器输出
    name: output
    write: DO04-03
  - name: test                 # 仅有 name 的中转项
  - reset                      # 只有 name 的简写方式，它等效于 `- {name: reset}`
  - comment: 外输泵停止状态
    name: stop
    read: NOT "DI02-15"        # 用表达式给该项赋值
  - comment: 温度高             # 完整写法的中转数据项
    name: temperature          # 自定义在DB中的名称
    read: '"pump485".TIT-1201' # 读值为S7表达式 这里读485通讯块中的温度
    write: '"WCS".temperature' # 写值为S7表达式 这里转存温度到水冷系统DB块中
    type: INT                  # S7 类型为 INT
    init: 10                   # 初始值为 10
    # 同时有 read 和 write 的项更多用于数值的运算后传递。
  - comment: 外输泵润滑系统工作状态
    name: lubrication_work
    read: [DI02-09, I5.0]      # 读值为S7符号的项
  - {name: PowerReady, read: DI02-12, comment: 高压到位}

  input:
  - test                       # data项名称作为输入项，与 `{value: test}` 等效
  - DI02-11                    # 符号引用作为输入项，与 `{value: DI02-11}` 等效
  - [DI02-14, I5.5]            # 符号定义作为输入项，与 `{value: [DI02-14, I5.5]}` 等效
  - '"PIT-1201".AH_Flag'       # SCL表达式作为该输入项，与 `{value: '"PIT-1201".AH_Flag'}` 等效
  - IL_pump.temperature > 100  # SCL表达式作为输入项，与 `{value: IL_pump.temperature > 100}` 等效

  - comment: 外输泵润滑系统
    trigger: falling
    value: lubrication_work                       # 触发过程值是一个 data 项名称
  - comment: 外输泵错误
    value: DI02-10                                # 触发过程值是一个S7符号
  - comment: 外输泵冷却水工作正常
    # 触发过程值是一个SCL表达式
    # 注意，在表达式中不可以直接写 data 中转项名称
    # 必须加DB前缀的形成完整SCL地址，比如下方的 WCS_work：
    value: '"IL_pump".WCS_work XOR MOT0101.run'   # 触发过程值是一个SCL表达式
    trigger: on
  - comment: 外输泵高压失电
    # 触发类型为断开，保证无高压时联锁报警
    trigger: off
    value: 'PowerReady'
  - and:               # 来油罐液位低
    - LIT0205A.AL_flag # 1#罐液位低
    - LIT0205B.AL_flag # 2#罐液位低

  reset:
  - reset              # data项名称 这里用来人工确认
  - DI02-13            # S7符号引用
  - Alarm_SL.reset     # SCL表达式

  output:
  - output             # 这是 data 项，也可以用S7符号或表达式
  - value: [DO04-02, Q6.1]
    reset: stop
  - value: IL_ESDBTN.test
    reset: NOT inputs  # 当所有输入信号消失时，自动复位
    comment: 输出至上级报警

# === 例3
- DB: [IL_sound, DB123, ~, 事件播报]
  $enable: false
  trigger:  on

  data:
  - {name: play, write: DO04-04, comment: 广播设备播放}
  - {name: stop, write: [DO04-06, Q6.5],  comment: 广播设备停止}
  - {name: WCounter_signal, read: DI02-16, comment: 计件器检测}
  - {name: comfirm, read: DI02-20, comment: 人工确认}
  - {name: RSTSWT, read: DI02-19, comment: 人工停播旋钮}

  input:
  - WCounter_signal           # 计件器检测
  - and:                      # 每一项的类型为: data项名称 | S7符号定义 | S7符号引用 | SCL表达式
    - comfirm                 # 人工确认
    - '"WCounter".fix'        # 检测异常
    trigger: on

  output:
  - play                      # 广播设备播放

  extra_code: |-
    // 记录播报次数
    IF IL_sound.output AND NOT "WCounter".follower THEN
        "WCounter".count := "WCounter".count + 1;
    ENDIF;
    "WCounter".follower := IL_sound.output;

- DB: IL_sound
  input:
  - RSTSWT
  output:
  - value: stop
    inversion: TRUE
    default: FALSE

loop_begin: |-
  // 这里的代码将在 Interlock_Loop 循环开始时执行
loop_end: |-
  // 这里的代码将在 Interlock_Loop 循环结束时执行
...
