扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

西门子SCL-Variant 类型队列实现

2025/08/23
2222
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

一、Variant 循环队列概述

循环队列是一种基于数组实现的先进先出(FIFO)数据结构,通过首尾指针的循环移动解决数组空间的浪费问题。而Variant 循环队列(又称 “变体循环队列”)则是对传统循环队列的扩展,其核心特性是支持存储不同数据类型的元素(如 Int、Real 等),实现异构数据的有序存储与访问。

核心特点

类型灵活性:无需预先限定存储元素的类型,可动态处理 Int、Real 等多种数据类型;

空间高效性:基于循环队列结构,避免数组 “假溢出” 问题,充分利用存储空间;

类型适配开销:需通过类型检测与转换机制实现多类型支持,可能引入额外的处理开销;

操作原子性:通过脉冲触发(上升沿检测)确保入队、出队等操作的单次执行。

二、实现原理与核心机制

1. 底层数据结构

Variant 循环队列的底层依托数组实现,通过两个指针(FrontRear)标记队列的头部与尾部:

Front:指向队头元素(出队时读取的位置);

Rear:指向队尾的下一个空闲位置(入队时写入的位置);

队列满判断:(Rear + 1) MOD 数组长度 = Front

队空判断:Rear = Front

2. 多类型支持机制

通过 Variant 类型(变体类型)实现多类型兼容,核心依赖两个操作:

VariantGet:从 Variant 变量中提取具体类型数据(如从 Variant 数组中提取 Int 数组);

VariantPut:将具体类型数据写入 Variant 变量(如将修改后的 Int 数组写回 Variant 变量)。

3. 脉冲触发机制

为避免操作信号持续有效导致的重复执行,通过上升沿检测(_P指令)实现脉冲触发,确保每个操作(入队、出队等)仅在信号从 0 变为 1 时执行一次。

三、详细实现步骤

核心逻辑代码(带注释)

以下代码基于结构化文本(ST)编写,实现了 Int 和 Real 类型的循环队列操作:

// 功能块:Variant循环队列实现(支持Int和Real类型)FUNCTION_BLOCK FB_VariantCircularQueueVAR_INPUT    var_arr: Variant;       // 存储队列数据的Variant数组(Int或Real)    var_data: Variant;      // 入队/出队的数据(Variant类型)    Join: BOOL;             // 入队触发信号(上升沿有效)    Leave: BOOL;            // 出队触发信号(上升沿有效)    Reset: BOOL;            // 错误复位信号(上升沿有效)    Clear: BOOL;            // 队列清零信号(上升沿有效)END_VARVAR_OUTPUT    Error: BOOL := 0;       // 错误标志(1=出错,0=正常)    ErrorInfo: WORD := 0;   // 错误代码(16#0001=队满,16#0002=队空)END_VARVAR    // 脉冲检测器(上升沿检测),确保操作仅执行一次    Join_P: R_TRIG;         // 入队信号上升沿检测    Leave_P: R_TRIG;        // 出队信号上升沿检测    Reset_P: R_TRIG;        // 复位信号上升沿检测    Clear_P: R_TRIG;        // 清零信号上升沿检测
    Front: INT := 0;        // 队头指针(指向队头元素)    Rear: INT := 0;         // 队尾指针(指向队尾下一个空闲位置)    size: INT;              // 数组长度
    // Int类型临时变量(用于类型转换)    arr_int: ARRAY[*] OF INT;   // 从var_arr提取的Int数组    data_int: INT;              // 临时存储Int类型数据
    // Real类型临时变量(用于类型转换)    arr_real: ARRAY[*] OF REAL; // 从var_arr提取的Real数组    data_real: REAL;            // 临时存储Real类型数据
    i: INT;                  // 循环计数器END_VAR// 主逻辑:仅处理数组类型的Variant变量IF IS_ARRAY(var_arr) THEN    // 检查输入是否为数组类型    // 区域1:脉冲触发处理(上升沿检测)    REGION 脉冲触发处理        // 对各操作信号进行上升沿检测,Q为1表示检测到上升沿        Join_P(CLK := Join);      // 入队信号上升沿检测        Join := 0;                // 重置输入信号(避免持续触发)        Leave_P(CLK := Leave);    // 出队信号上升沿检测        Leave := 0;               // 重置输入信号        Reset_P(CLK := Reset);    // 复位信号上升沿检测        Reset := 0;               // 重置输入信号        Clear_P(CLK := Clear);    // 清零信号上升沿检测        Clear := 0;               // 重置输入信号    END_REGION    // 区域2:Int类型循环队列处理    REGION Int类型循环队列处理        IF TypeOfElements(var_arr) = INT THEN  // 判断数组元素类型为Int            // 1. 获取数组大小(转换为INT类型)            size := UDINT_TO_INT(CountOfElements(var_arr));
            // 2. 从Variant数组中提取Int数组到临时变量arr_int            VariantGet(SRC := var_arr, DST => arr_int);
            // 3. 从Variant数据中提取Int数据到临时变量data_int            VariantGet(SRC := var_data, DST => data_int);            // 子区域:入队操作(仅在检测到上升沿时执行)            REGION 入队操作                IF Join_P.Q THEN  // 检测到入队信号上升沿                    // 队满判断:(Rear + 1) MOD 数组长度 = Front                    IF (Rear + 1) MOD size = Front THEN                        Error := 1;               // 置位错误标志                        ErrorInfo := 16#0001;     // 错误代码:队满                        RETURN;                   // 终止本次执行                    END_IF;
                    // 执行入队:将数据写入Rear指向的位置                    arr_int[Rear] := data_int;                    // 更新队尾指针:循环移动(取模确保不越界)                    Rear := (Rear + 1) MOD size;                END_IF;            END_REGION            // 子区域:出队操作(仅在检测到上升沿时执行)            REGION 出队操作                IF Leave_P.Q THEN  // 检测到出队信号上升沿                    // 队空判断:Rear = Front                    IF Rear = Front THEN                        Error := 1;               // 置位错误标志                        ErrorInfo := 16#0002;     // 错误代码:队空                        RETURN;                   // 终止本次执行                    END_IF;
                    // 执行出队:从Front指向的位置读取数据                    data_int := arr_int[Front];                    arr_int[Front] := 0;          // 清空出队位置(可选)                    // 更新队头指针:循环移动                    Front := (Front + 1) MOD size;
                    // 将出队数据写回Variant变量var_data                    VariantPut(SRC := data_int, DST := var_data);                END_IF;            END_REGION            // 子区域:复位操作(清除错误状态)            REGION 复位操作                IF Reset_P.Q THEN  // 检测到复位信号上升沿                    Error := 0;        // 清除错误标志                    ErrorInfo := 0;    // 清除错误代码                END_IF;            END_REGION            // 子区域:清零操作(清空队列并重置指针)            REGION 清零操作                IF Clear_P.Q THEN  // 检测到清零信号上升沿                    // 遍历数组,清空所有元素                    FOR i := 0 TO size - 1 DO                        arr_int[i] := 0;                    END_FOR;                    // 重置队头、队尾指针(恢复队空状态)                    Front := 0;                    Rear := 0;                END_IF;            END_REGION            // 将修改后的Int数组写回Variant变量var_arr            VariantPut(SRC := arr_int, DST => var_arr);        END_IF;    END_REGION    // 区域3:Real类型循环队列处理(逻辑与Int类型一致,仅类型不同)    REGION Real类型循环队列处理        IF TypeOfElements(var_arr) = REAL THEN  // 判断数组元素类型为Real            // 1. 获取数组大小            size := UDINT_TO_INT(CountOfElements(var_arr));
            // 2. 从Variant数组中提取Real数组到临时变量arr_real            VariantGet(SRC := var_arr, DST => arr_real);
            // 3. 从Variant数据中提取Real数据到临时变量data_real            VariantGet(SRC := var_data, DST => data_real);            // 子区域:入队操作            REGION 入队操作                IF Join_P.Q THEN  // 检测到入队信号上升沿                    // 队满判断                    IF (Rear + 1) MOD size = Front THEN                        Error := 1;                        ErrorInfo := 16#0001;  // 队满                        RETURN;                    END_IF;
                    // 执行入队                    arr_real[Rear] := data_real;                    Rear := (Rear + 1) MOD size;                END_IF;            END_REGION            // 子区域:出队操作            REGION 出队操作                IF Leave_P.Q THEN  // 检测到出队信号上升沿                    // 队空判断                    IF Rear = Front THEN                        Error := 1;                        ErrorInfo := 16#0002;  // 队空                        RETURN;                    END_IF;
                    // 执行出队                    data_real := arr_real[Front];                    arr_real[Front] := 0.0;  // 清空出队位置(可选)                    Front := (Front + 1) MOD size;
                    // 将出队数据写回Variant变量                    VariantPut(SRC := data_real, DST := var_data);                END_IF;            END_REGION            // 子区域:复位操作            REGION 复位操作                IF Reset_P.Q THEN                    Error := 0;                    ErrorInfo := 0;                END_IF;            END_REGION            // 子区域:清零操作            REGION 清零操作                IF Clear_P.Q THEN                    // 遍历数组,清空所有元素                    FOR i := 0 TO size - 1 DO                        arr_real[i] := 0.0;                    END_FOR;                    // 重置指针                    Front := 0;                    Rear := 0;                END_IF;            END_REGION            // 将修改后的Real数组写回Variant变量            VariantPut(SRC := arr_real, DST => var_arr);        END_IF;    END_REGIONELSE    // 输入不是数组类型,直接返回(不执行任何操作)    RETURN;END_IF;

2. 数据块(DB)创建

数据块用于存储队列的实例数据(如数组、指针状态等),需根据实际需求定义:

若处理 Int 类型队列,创建包含var_arr(Int 数组)和var_data(Int 变量)的 Variant 类型数据块;

若处理 Real 类型队列,创建包含var_arr(Real 数组)和var_data(Real 变量)的 Variant 类型数据块。

3. 组织块(OB)调用

在组织块(如 OB1,主循环组织块)中调用功能块FB_VariantCircularQueue,并关联数据块:

// OB1:主循环组织块VAR    // 实例化功能块    queue: FB_VariantCircularQueue;    // 数据块(以Int类型为例)    db_queue: DB_IntQueue;  // 包含var_arr(ARRAY[0..7] OF INT)和var_data(INT)    // 操作信号(可通过HMI或其他逻辑触发)    enqueue: BOOL;  // 入队触发    dequeue: BOOL;  // 出队触发    reset_err: BOOL;// 错误复位    clear_queue: BOOL;// 队列清零END_VAR// 调用功能块,关联输入输出queue(    var_arr := db_queue.var_arr,    var_data := db_queue.var_data,    Join := enqueue,    Leave := dequeue,    Reset := reset_err,    Clear := clear_queue,    Error => ,    ErrorInfo => );

四、仿真调试要点

类型一致性检查:确保var_arr的元素类型与var_data的类型一致(如均为 Int 或均为 Real),否则会导致类型转换错误;

边界条件测试:队空时执行出队操作,验证是否触发Error=1ErrorInfo=16#0002;队满时执行入队操作,验证是否触发Error=1ErrorInfo=16#0001

指针循环验证:当指针到达数组末尾时,检查是否正确回绕到起始位置(如Rear从 7(数组长度 8)变为 0);脉冲信号测试:确保单次触发信号(上升沿)仅执行一次操作,避免连续触发导致的数据错误。

五、总结

Variant 循环队列通过 Variant 类型的灵活性与循环队列的空间高效性,实现了异构数据的有序管理。其核心在于通过VariantGet/VariantPut实现类型转换,结合脉冲触发确保操作原子性。该实现适用于需要动态处理多类型数据的场景(如工业控制中的混合信号采集),但需注意类型检查与转换的开销,在高性能场景下可针对特定类型优化实现。

西门子

西门子

德国西门子股份公司(SIEMENS AG)创立于1847年,是全球电子电气工程领域的领先企业。西门子自1872年进入中国,140余年来以创新的技术、卓越的解决方案和产品坚持不懈地对中国的发展提供全面支持,并以出众的品质和令人信赖的可靠性、领先的技术成就、不懈的创新追求,确立了在中国市场的领先地位。

德国西门子股份公司(SIEMENS AG)创立于1847年,是全球电子电气工程领域的领先企业。西门子自1872年进入中国,140余年来以创新的技术、卓越的解决方案和产品坚持不懈地对中国的发展提供全面支持,并以出众的品质和令人信赖的可靠性、领先的技术成就、不懈的创新追求,确立了在中国市场的领先地位。收起

查看更多

相关推荐