﻿FUNCTION_BLOCK "Valve_Proc"
TITLE='电动执行机构，可选阀位反馈'
VERSION:'1.3'
KNOW_HOW_PROTECT
AUTHOR:Goosy
NAME:ValvePrc
FAMILY:GooLib

CONST
    S7_ZERO := 0;
    S7_SPAN := 27648;
    S7_AI_MIN := -32768;
    S7_AI_MIN_WORD := W#16#8000;
    S7_AI_MAX := 32767;
    S7_AI_MAX_WORD := W#16#7FFF;
    STOP_STATUS := W#16#0;
    CLOSE_STATUS := W#16#1;
    OPEN_STATUS := W#16#2;
    STOPPING_STATUS := W#16#4;
    CLOSING_STATUS := W#16#8;
    OPENNING_STATUS := W#16#10;
    POSITION_STATUS := W#16#20;
    ZERO := 0.0;
    SPAN := 100.0;
    INVALID_VALUE := -100.0;
END_CONST

VAR_INPUT
    CP {S7_m_c := 'true'} : BOOL;                     // 全关位
    OP {S7_m_c := 'true'} : BOOL;                     // 全开位
    remote {S7_m_c := 'true'} : BOOL := TRUE;         // 远程
    error {S7_m_c := 'true'} : BOOL;                  // 错误
    AI {S7_m_c := 'true'} : WORD := S7_AI_MIN_WORD;   // 阀位采集值
    VP_raw AT AI : INT;
    zero_raw {S7_m_c := 'true'} : INT := S7_ZERO;     // 零点原始值（4mA对应数值）0
    span_raw {S7_m_c := 'true'} : INT := S7_SPAN;     // 极值原始值（20mA对应数值）27648
    overflow_SP {S7_m_c := 'true'} : INT := 28000;    // 上溢出值
    underflow_SP {S7_m_c := 'true'} : INT := -500;    // 下溢出值
    VP_SP {S7_m_c := 'true'} : REAL := 50.0;          // 设定值
    FT_zone {S7_m_c := 'true'} : REAL := 0.5;         // 定位容错区
    action_time {S7_m_c := 'true'} : DINT := 100000;  // 动作时间，完成阀动作的最长时间，无阀位时在该时间后判断是否动作失败 (单位毫秒)
    action_DR AT action_time : TIME;
    signal_time {S7_m_c := 'true'} : DINT := 500;     // 信号时间，停止等信号要保持持续的最短时间，有阀位时在该时间后判断阀是否离线 (单位毫秒 阀位有效时)
    signal_DR AT signal_time : TIME;
END_VAR

VAR_OUTPUT
    close_action {S7_m_c := 'true'} : BOOL;   // 正在执行关阀
    open_action {S7_m_c := 'true'} : BOOL;    // 正在执行开阀
    stop_action {S7_m_c := 'true'} : BOOL;    // 停止执行开关阀
    control_action {S7_m_c := 'true'} : BOOL; // 2位单线控制 1：开阀 0：关阀
    valve_error {S7_m_c := 'true'} : BOOL;    // 阀体故障或信号故障，无法操作
    VP_error {S7_m_c:='true'} : BOOL;         // 阀位值无效，断路、短路、溢出
    action_success {S7_m_c := 'true'} : BOOL; // 阀动作成功，当 action_success action_error 都不为 true 时，表示不能判断
    action_error {S7_m_c := 'true'} : BOOL;   // 执行错误，即开、关、停在指定时间后无反馈
    action {S7_m_c := 'true'} : BOOL;         // 阀动作执行中
    VP {S7_m_c := 'true'} : REAL;             // 阀位
END_VAR

VAR_IN_OUT
    close_CMD {S7_m_c := 'true'} : BOOL;      // 关阀命令
    open_CMD {S7_m_c := 'true'} : BOOL;       // 开阀命令
    position_CMD {S7_m_c := 'true'} : BOOL;   // 定位命令
    stop_CMD {S7_m_c := 'true'} : BOOL;       // 保位命令，即停止，保持当前阀位
END_VAR

VAR
    executable      : BOOL; // 可执行
    action_trigger  : BOOL; // 阀动作触发
    signal          : BOOL; // 阀信号中
    mode            : WORD; // 当前模式
    last_mode       : WORD; // 上一循环时的模式
    last_VP         : REAL; // 上一次阀位
    signal_TOF : TOF;
    action_TOF : TOF;
END_VAR

VAR_TEMP
    ERR : BOOL;             // 测量错误
END_VAR

BEGIN
//**********************
// 阀位反馈
//**********************

// 判断输入值的有效性
IF VP_raw = S7_AI_MIN OR VP_raw = S7_AI_MAX THEN
    // 非测量输入
    ERR := TRUE;
ELSIF VP_raw > overflow_SP OR VP_raw < underflow_SP THEN
    // 溢出
    ERR := TRUE;
ELSE
    // 正常输入
    ERR := FALSE;
END_IF;

// 设定阀位值及其有效性
VP_error := ERR;
IF ERR THEN
    VP := INVALID_VALUE; // 无效时不计算
ELSE
    VP := (VP_raw - zero_raw)
        * (SPAN - ZERO)
        / (span_raw - zero_raw)
        + ZERO;
END_IF;

//**********************
// 阀动作处理
//**********************

// 准备动作变量
valve_error := error OR (CP AND OP); //阀故障
executable := remote AND NOT valve_error; // 可执行
action := mode = STOPPING_STATUS OR mode = OPENNING_STATUS OR mode = CLOSING_STATUS OR mode = POSITION_STATUS;
IF NOT executable THEN
    // 切换至停止状态
    // 开到位和关到位情况会在后继代码中做修正，无须在此切换
    IF action THEN
        mode := STOP_STATUS;
    END_IF;
    // 输出线圈会在后继代码中复位
    signal := FALSE;
    action := FALSE;
    action_success := FALSE;
    action_error := FALSE;
    close_CMD := FALSE;
    open_CMD := FALSE;
    position_CMD := FALSE;
    stop_CMD := FALSE;
    // 重置动作计时
    signal_TOF.STATE := B#16#0;
    action_TOF.STATE := B#16#0;
END_IF;
// action 上升沿
action_trigger := last_mode <> mode AND action; // action 已隐含 executable
last_mode := mode;
IF action_trigger THEN // 重置离线错误，记录动作前的阀位
    action_success := FALSE;
    action_error := FALSE;
    IF NOT VP_error THEN
        last_VP := VP;
    END_IF;
END_IF;

// 动作计时
signal_TOF(
    IN := action_trigger,
    PT := signal_DR);
action_TOF(
    IN := action_trigger,
    PT := action_DR);

// 停止命令优先
IF stop_CMD THEN
    IF mode = STOPPING_STATUS THEN
        mode := STOP_STATUS; // 确保停止状态可以继续响应 stop_CMD
    ELSE
        mode := STOPPING_STATUS;
        stop_CMD := FALSE;
    END_IF;
    close_CMD := FALSE;
    open_CMD := FALSE;
    position_CMD := FALSE;
END_IF;

// 停阀动作
IF mode = STOPPING_STATUS THEN
    open_action := FALSE;
    close_action := FALSE;
    stop_action := TRUE;
END_IF;
// 复位动作
IF mode = STOP_STATUS THEN
    open_action := FALSE;
    close_action := FALSE;
    stop_action := FALSE;
END_IF;

// 全开状态的判定和动作
IF OP AND mode <> CLOSING_STATUS AND mode <> POSITION_STATUS THEN
    IF mode = OPENNING_STATUS THEN
        action_success := TRUE;
    END_IF;
    mode := OPEN_STATUS;
END_IF;
// 复位动作
IF mode = OPEN_STATUS THEN
    open_CMD := FALSE;
    close_action := FALSE;
    open_action := FALSE;
    control_action := TRUE;
    stop_action := FALSE;
END_IF;

// 全关状态的判定
IF CP AND mode <> OPENNING_STATUS AND mode <> POSITION_STATUS THEN
    IF mode = CLOSING_STATUS THEN
        action_success := TRUE;
    END_IF;
    mode := CLOSE_STATUS;
END_IF;
// 复位动作
IF mode = CLOSE_STATUS THEN
    close_CMD := FALSE;
    open_action := FALSE;
    close_action := FALSE;
    control_action := FALSE;
    stop_action := FALSE;
END_IF;

// 开阀
IF open_CMD THEN
    IF mode = OPENNING_STATUS THEN
        mode := STOP_STATUS; // 确保 OPENNING_STATUS 可以继续响应 open_CMD
    ELSE
        mode := OPENNING_STATUS;
        open_CMD := FALSE;
    END_IF;
    close_CMD := FALSE;
    position_CMD := FALSE;
END_IF;
// 动作
IF mode = OPENNING_STATUS THEN
    close_action := FALSE;
    open_action := TRUE;
    control_action := TRUE;
    stop_action := FALSE;
END_IF;

// 关阀
IF close_CMD THEN
    IF mode = CLOSING_STATUS THEN
        mode := STOP_STATUS; // 确保 CLOSING_STATUS 可以继续响应 close_CMD
    ELSE
        mode := CLOSING_STATUS;
        close_CMD := FALSE;
    END_IF;
    open_CMD := FALSE;
    position_CMD := FALSE;
END_IF;
// 动作
IF mode = CLOSING_STATUS THEN
    close_action := TRUE;
    open_action := FALSE;
    control_action := FALSE;
    stop_action := FALSE;
END_IF;

// 指定阀位控制
IF VP_error THEN // 阀位无效时关闭指定阀位命令
    position_CMD := FALSE;
    IF mode = POSITION_STATUS THEN
        action_error := TRUE;
        mode := STOPPING_STATUS;
    END_IF;
END_IF;
IF position_CMD THEN
    IF mode = POSITION_STATUS THEN
        mode := STOP_STATUS; // 确保 POSITION_STATUS 可以继续响应 position_CMD
    ELSE
        mode := POSITION_STATUS;
        position_CMD := FALSE;
    END_IF;
END_IF;
// 动作
IF mode = POSITION_STATUS THEN
    IF VP > VP_SP + FT_zone THEN //当设定值容错区低于阀位，关阀动作
        close_action := TRUE;
        open_action := FALSE;
        stop_action := FALSE;
    ELSIF VP < VP_SP - FT_zone THEN //当设定值容错区高于阀位，开阀动作
        close_action := FALSE;
        open_action := TRUE;
        stop_action := FALSE;
    ELSE //当设定值在阀位容错区内，停止动阀，复位定位命令
        close_action := FALSE;
        open_action := FALSE;
        position_CMD := FALSE;
        action_success := TRUE;
        mode := STOPPING_STATUS;
    END_IF;
END_IF;

// 根据有无阀位，取不同的动作无响应时间

// 信号结束时判断执行结果
// signal 已隐含 executable
IF NOT signal_TOF.Q AND signal THEN
    IF mode = STOPPING_STATUS AND NOT VP_error THEN // 有阀位，判断停止动作结果
        action_success := VP < last_VP + FT_zone AND VP > last_VP - FT_zone;
        action_error := NOT action_success;
    ELSIF mode = STOPPING_STATUS THEN // 无阀位，停止动作无法判断，直接切换状态
        mode := STOP_STATUS;
        action_TOF.STATE := B#16#0;
    ELSIF action AND NOT VP_error THEN // 其它3个动作在信号结束时只能有阀位时判断失败情况
        IF mode = CLOSING_STATUS THEN // 关动作判断是否失败
            action_error := VP > last_VP - FT_zone;
        ELSIF mode = OPENNING_STATUS THEN // 开动作判断是否失败
            action_error := VP < last_VP + FT_zone;
        ELSIF mode = POSITION_STATUS THEN // 定位动作判断是否失败
            // 这里无需考虑目标阀位对动作的影响，因为进入目标阀位后必定不在 POSITION_STATUS 状态下。
            action_error := (VP < last_VP + FT_zone) AND (VP > last_VP - FT_zone);
        END_IF;
    END_IF;
END_IF;
signal := signal_TOF.Q;

// 提前结束无须本次检查
IF action_success THEN
    action_error := FALSE;
END_IF;
IF (action_error OR action_success) AND action THEN
    mode := STOP_STATUS;
    action := FALSE; // 用于屏蔽下方超时检查
    action_TOF.STATE := B#16#0;
END_IF;
// 动作超时判断执行情况(即动作未能提前结束)
IF NOT action_TOF.Q AND action THEN
    // 隐含 NOT action_error AND NOT action_success
    IF mode = STOPPING_STATUS THEN
        // 无阀位时，STOPPING_STATUS 才会延伸到这里
        // 不作检查，仅仅切换至已停状态
        mode := STOP_STATUS;
    ELSE
        // 其余 CLOSING_STATUS OPENNING_STATUS POSITION_STATUS 3种状态
        // 这3种状态能延伸到这里，一定因为动作失败
        action_success := FALSE;
        action_error := TRUE;
        mode := STOP_STATUS;
    END_IF;
END_IF;
END_FUNCTION_BLOCK
