源码加更01_拿到 MqttClient 源码后,先看工程入口和数据模型

源码加更01_拿到 MqttClient 源码后,先看工程入口和数据模型

!abstract 这一篇把 MqttClient V1.0 的工程入口、全局常量和数据结构一次性摆清楚。读者先知道这套开源客户端由哪些对象组成,再进入后面状态机和报文细节。

适合谁收藏

  • 第一次打开 MqttClient V1.0 源码,不知道从哪里读起的工程师。
  • 想先建立源码地图,再看 CONNECT、PUBLISH 和 QoS 细节的读者。
  • 需要把 DUT、GVL、PRG 和 FB 边界讲给团队听的自动化工程师。

本篇核心图

读图重点:先看源码对象之间的职责边界,再看数据、状态和错误如何沿着调用链流动。源码加更不是把文件名列出来,而是把完整代码、工程意图和验证口径一起讲清楚。

先给结论

先看入口和数据模型,不要一上来就钻某个报文方法。MQTT 报文是结果,工程能不能稳定跑,先由容量、状态、事务和诊断模型决定。

从理论到代码实现链路

MQTT 标准给的是报文类型、固定头、可变头、载荷、QoS 交互和会话语义;PLC 工程真正要解决的是周期扫描、缓冲区长度、错误锁存、在线变量、连接重入和现场可诊断性。

所以这套开源实现不能只按协议章节拆,也不能只按文件名拆。正确读法是把标准约束翻译成程序对象:入口程序负责给命令和观测点,GVL 和 DUT 定义容量与数据模型,主功能块负责调度状态机,构建方法负责出站报文,处理方法负责入站报文,辅助方法负责长度、队列、事务、主题和诊断边界。

本篇公开 PRG_Test、GVL_Mqtt 和全部 MQTT DUT。这些代码不是配角,它们决定后面所有报文和状态机能装下什么信息。

再往下一层看,这里其实有两条线同时存在。第一条是协议线:固定头、Remaining Length、PacketId、QoS、Topic、Payload 和 Reason Code 必须能按 MQTT 规则组合起来。第二条是 PLC 工程线:每个周期只能推进有限步骤,所有中间状态都要能被在线变量观察,所有错误都要能被锁存并归类,所有缓冲区长度都要在写入前被检查。

这就是源码加更必须完整公开的原因。只给几段核心片段,读者最多能看懂某个判断;把完整对象放出来,读者才能看到对象之间如何传递状态、长度、错误和诊断信息。完整源码讲解不是为了堆代码,而是为了让读者能从标准约束一路追到可运行的 ST 对象,再从现场现象反向定位到具体边界。

本篇公开的完整源码范围

序号 源码对象 讲解重点
1 PRG_Test.st 源码对象职责和验证边界
2 GVL_Mqtt.st 源码对象职责和验证边界
3 E_MqttConnectFlag.st 枚举边界,决定状态、报文类型或返回码的可读性
4 E_MqttInflightState.st 枚举边界,决定状态、报文类型或返回码的可读性
5 E_MqttPacketType.st 枚举边界,决定状态、报文类型或返回码的可读性
6 E_MqttPropertyId.st 枚举边界,决定状态、报文类型或返回码的可读性
7 E_MqttPropertyType.st 枚举边界,决定状态、报文类型或返回码的可读性
8 E_MqttQoS.st 枚举边界,决定状态、报文类型或返回码的可读性
9 E_MqttReasonCode.st 枚举边界,决定状态、报文类型或返回码的可读性
10 E_MqttState.st 枚举边界,决定状态、报文类型或返回码的可读性
11 E_MqttSubOption.st 枚举边界,决定状态、报文类型或返回码的可读性
12 E_MqttVersion.st 枚举边界,决定状态、报文类型或返回码的可读性
13 E_ReasonCode.st 枚举边界,决定状态、报文类型或返回码的可读性
14 ST_MqttInflight.st 结构体边界,决定在线观察和事务记录能否稳定表达
15 ST_MqttInflightMessage.st 结构体边界,决定在线观察和事务记录能否稳定表达
16 ST_MqttMessage.st 结构体边界,决定在线观察和事务记录能否稳定表达
17 ST_MqttProperty.st 结构体边界,决定在线观察和事务记录能否稳定表达
18 ST_MqttSubscription.st 结构体边界,决定在线观察和事务记录能否稳定表达
19 ST_MqttTopicAlias.st 结构体边界,决定在线观察和事务记录能否稳定表达

怎么读这些源码

第一遍只看对象职责:这个文件解决哪一层问题,是入口、模型、状态、构建、接收、事务,还是诊断。

第二遍看边界变量:长度、索引、PacketId、QoS、状态枚举、错误码、缓冲区水位和在线观测量。PLC 通信代码最怕的是"能跑但不可诊断",所以每个关键对象都要问一句:现场出问题时,我能不能从它留下的变量看出原因。

第三遍再看具体语句。源码全部公开,不等于读者要从第一行顺序读到最后一行。更稳的方式是用图和表先建立地图,再回到完整代码里确认每个边界确实落地。

工程验证路径

导入后先编译 DUT 和 GVL,再运行 PRG_Test。第一轮只看客户端实例、连接参数、订阅主题、接收消息和错误诊断是否都有可观测出口。

本篇完整开源代码

完整代码 1:PRG_Test.st

这一段完整公开 PRG_Test.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : PRG_Test
/// 功能      : MQTT 客户端测试程序
/// 说明      : 用于驱动 FB_MqttClient 进行连接、发布、订阅与接收测试
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-05
/// 版本      : V1.0
/// =======================================================================
{attribute 'hide_all_locals'}
PROGRAM PRG_Test
VAR
    bInit                    : BOOL := TRUE;                                                    // 初始化执行标志
    fbMqttClient            : FB_MqttClient;                                                    // MQTT 客户端实例
    bEnable                  : BOOL := TRUE;                                                    // 使能客户端
    bConnect                 : BOOL;                                                            // 连接命令
    sBrokerIP                : STRING := '192.168.20.2';                                     // Broker IP 地址
    uiPort                   : UINT := 1883;                                                   // Broker 提供 MQTT 服务的监听端口号
    sClientID                : STRING := 'CodeSys_PLC';                                        // 客户端标识符
    sUsername                : STRING := 'CodeSys';                                                   // 用户名
    sPassword                : STRING := '456852';                                                   // 密码
    eVersion                 : E_MqttVersion := E_MqttVersion.byMqttVersion50;                // MQTT 协议版本
    bCleanSession            : BOOL := TRUE;                                                   // 清理会话标志
    uiKeepAlive              : UINT := 60;                                                     // 客户端心跳保活周期[s]
    bUseSSL                  : BOOL := FALSE;                                                  // 是否启用 SSL
    bAutoReconnect           : BOOL := TRUE;                                                   // 是否自动重连
    uiReconnectDelay         : UINT := 5000;                                                   // 自动重连前的等待时间[ms]
    udiSessionExpiry         : UDINT := 0;                                                     // "Broker 在你断线后,愿意帮你保留会话多久"的秒数[s]
    uiReceiveMax             : UINT := 65535;                                                  // 客户端告诉 Broker"我最多允许同时压多少条 QoS>0 未完成消息给我"
    udMaxPacketSize          : UDINT := 4096;                                                  // 客户端声明可接收的最大 MQTT 报文长度[byte]
    bWillFlag                : BOOL := TRUE;                                                   // 是否启用遗嘱消息
    sWillTopic               : STRING(GVL_Mqtt.cnMaxTopicLen) := 'CodeSys';                    // 遗嘱主题
    sWillMessage             : STRING(GVL_Mqtt.cnMaxPayloadSize) := 'CodeSys Offline';         // 遗嘱消息内容
    eWillQoS                 : E_MqttQoS := E_MqttQoS.byQoS2;                                 // 遗嘱消息 QoS
    bWillRetain              : BOOL := FALSE;                                                  // 遗嘱消息保留标志
    bPublish                 : BOOL;                                                            // 发布命令
    sPubTopic                : STRING(GVL_Mqtt.cnMaxTopicLen) := 'CodeSys';                    // 发布主题
    sPubPayload              : STRING(GVL_Mqtt.cnMaxPayloadSize) := 'CodeSys';                 // 发布载荷
    ePubQoS                  : E_MqttQoS := E_MqttQoS.byQoS2;                                 // 发布 QoS
    bPubRetain               : BOOL := FALSE;                                                  // 发布保留标志
    bSubscribe               : BOOL;                                                            // 订阅命令
    bUnsubscribe             : BOOL;                                                            // 取消订阅命令
    sSubTopic                : STRING(GVL_Mqtt.cnMaxTopicLen) := 'CodeSys';                    // 订阅主题
    sUnsubTopic              : STRING(GVL_Mqtt.cnMaxTopicLen) := 'CodeSys';                    // 取消订阅主题
    eSubQoS                  : E_MqttQoS := E_MqttQoS.byQoS2;                                 // 订阅请求 QoS
    udiSubscriptionId        : UDINT := 0;                                                    // MQTT 5.0 订阅标识符,便于区分消息属于哪次订阅
    eMqttState               : E_MqttState;                                                    // 客户端当前状态
    bIsConnected             : BOOL;                                                            // TCP 连接状态
    bMqttConnected           : BOOL;                                                            // MQTT 连接状态
    bError                   : BOOL;                                                            // 错误标志
    eErrorID                 : NBS.ERROR;                                                       // 错误码
    sDiagMsg                 : STRING;                                                          // 诊断信息
    aSubscriptions           : ARRAY[1..GVL_Mqtt.cnMaxSubscriptions] OF ST_MqttSubscription;  // 订阅列表
    uiSubscriptionCount      : UINT;                                                            // 当前仍由客户端本地订阅表维护的有效主题数量
    sRecTopic                : STRING(GVL_Mqtt.cnMaxTopicLen);                                 // 最新接收主题
    sRecPayload              : STRING(GVL_Mqtt.cnMaxPayloadSize);                              // 最新接收载荷
    aRecTopicList            : ARRAY[0..GVL_Mqtt.cnMaxHistory] OF STRING(GVL_Mqtt.cnMaxTopicLen); // 接收主题历史
    aRecPayloadList          : ARRAY[0..GVL_Mqtt.cnMaxHistory] OF STRING(GVL_Mqtt.cnMaxPayloadSize); // 接收载荷历史
    byReceivedQoS            : BYTE;                                                            // 最新接收 QoS
    bReceivedRetain          : BOOL;                                                            // 最新接收保留标志
END_VAR

// === IMPLEMENTATION ===
/// 该测试程序不做复杂初始化动作,
/// 仅保留一个单次起始位,方便后续扩展测试序列。
IF bInit THEN
    bInit := FALSE;
END_IF

/// MQTT客户端
fbMqttClient(
    /// =================================== Config ====================================
    bEnable                := bEnable,
    bConnect               := bConnect,
    sBrokerIP              := sBrokerIP,
    uiPort                 := uiPort,
    sClientID              := sClientID,
    sUsername              := sUsername,
    sPassword              := sPassword,
    eVersion               := eVersion,
    bCleanSession          := bCleanSession,
    uiKeepAlive            := uiKeepAlive,
    bUseSSL                := bUseSSL,
    bAutoReconnect         := bAutoReconnect,
    uiReconnectDelay       := uiReconnectDelay,
    udiSessionExpiry       := udiSessionExpiry,
    uiReceiveMax           := uiReceiveMax,
    udMaxPacketSize        := udMaxPacketSize,

    /// =================================== 遗嘱 ====================================
    bWillFlag              := bWillFlag,
    sWillTopic             := sWillTopic,
    sWillMessage           := sWillMessage,
    eWillQoS               := eWillQoS,
    bWillRetain            := bWillRetain,

    /// =================================== 发布 ====================================
    bPublish               := bPublish,
    sPubTopic              := sPubTopic,
    sPubPayload            := sPubPayload,
    ePubQoS                := ePubQoS,
    bPubRetain             := bPubRetain,

    /// =================================== 订阅 ====================================
    bSubscribe             := bSubscribe,
    sSubTopic              := sSubTopic,
    eSubQoS                := eSubQoS,
    udiSubscriptionId      := udiSubscriptionId,
    bUnsubscribe           := bUnsubscribe,
    sUnsubTopic            := sUnsubTopic,

    /// =================================== 状态 ====================================
    eState                 => eMqttState,
    bIsConnected           => bIsConnected,
    bMqttConnected         => bMqttConnected,
    bError                 => bError,
    eErrorID               => eErrorID,
    sDiagMsg               => sDiagMsg,
    aSubscriptions         => aSubscriptions,
    uiSubscriptionCount    => uiSubscriptionCount,
    sRecTopic              => sRecTopic,
    sRecPayload            => sRecPayload,
    aRecTopicList          => aRecTopicList,
    aRecPayloadList        => aRecPayloadList,
    byReceivedQoS          => byReceivedQoS,
    bReceivedRetain        => bReceivedRetain);

完整代码 2:GVL_Mqtt.st

这一段完整公开 GVL_Mqtt.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : GVL_Mqtt
/// 功能      : MQTT 协议全局常量定义
/// 说明      : 提供 MQTT 3.1.1 / 5.0 编解码、状态机、缓存和属性处理所需常量
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-05
/// 版本      : V1.0
/// =======================================================================
{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
    /// ===================================================================
    /// 缓冲区、容量与循环保护
    /// ===================================================================
    cnSendBufferSize              : UINT   := 4096;        // MQTT 发送缓冲区容量[byte]
    cnRecvBufferSize              : UINT   := 4096;        // MQTT 接收缓冲区容量[byte]
    cnMaxSubscriptions            : UINT   := 32;          // 本地订阅表允许登记的最大主题数量
    cnMaxInflight                 : UINT   := 10;          // 本地可同时跟踪的最大 QoS>0 在途消息数量
    cnMaxTopicAlias               : UINT   := 16;          // 本地支持登记的最大主题别名数量
    cnMaxHistory                  : UINT   := 50;          // 接收消息历史列表允许保留的最大条目数
    cnMaxPropertyLoop             : UINT   := 100;         // 解析 MQTT 5.0 属性时允许的最大循环次数

    /// ===================================================================
    /// 超时、重试与重连节拍
    /// ===================================================================
    cnNetworkTimeout              : TIME   := T#10S;       // 网络层读写等待超时时间[ms]
    cnConnectTimeout              : TIME   := T#30S;       // 建立 MQTT 会话的最大等待时间[ms]
    cnResponseTimeout             : TIME   := T#5S;        // 等待服务器响应报文的默认超时时间[ms]
    cnPublishTimeout              : TIME   := T#10S;       // 等待发布流程完成的默认超时时间[ms]
    cnInflightTimeout             : TIME   := T#10S;       // 在途 QoS 报文判定超时的时间阈值[ms]
    cnMaxRetries                  : UINT   := 5;           // 单条在途消息允许的最大重试次数
    cnRetryInterval               : TIME   := T#3S;        // 两次重试之间的等待间隔[ms]
    cnReconnectBase               : TIME   := T#1S;        // 自动重连基础退避时间[ms]
    cnReconnectMax                : TIME   := T#60S;       // 自动重连最大退避时间[ms]

    /// ===================================================================
    /// 协议基础字符串、端口与长度上限
    /// ===================================================================
    cnWildcardMulti               : STRING := '#';         // 多层通配符
    cnWildcardSingle              : STRING := '+';         // 单层通配符
    cnTopicSeparator              : STRING := '/';         // 主题分隔符
    cnProtocolName                : STRING := 'MQTT';      // 协议名称
    cnProtocolLevelV311           : BYTE   := 4;           // MQTT 3.1.1 协议级别
    cnProtocolLevelV50            : BYTE   := 5;           // MQTT 5.0 协议级别
    cnProtocolLevel               : BYTE   := 5;           // 默认协议级别
    cnDefaultPort                 : UINT   := 1883;        // MQTT 默认非加密监听端口号
    cnDefaultPortTls              : UINT   := 8883;        // MQTT TLS 默认监听端口号
    cnMaxPacketSize               : UDINT  := 268435455;   // MQTT 协议允许的最大报文长度[byte]
    cnMaxPayloadSize              : UINT   := 2048;        // 本库默认允许的最大消息载荷长度[byte]
    cnMaxTopicLen                 : UINT   := 256;         // 本库默认允许的最大主题字符串长度[char]
    cnMaxClientIdLen              : UINT   := 256;         // ClientId 允许的最大字符串长度[char]
    cnMaxStringLen                : UINT   := 65535;       // MQTT UTF-8 字符串允许的最大长度[byte]

    /// ===================================================================
    /// 固定报头与 CONNECT / CONNACK 标志位
    /// ===================================================================
    cnHdrTypeMask                 : BYTE   := 16#F0;       // 固定报头类型掩码
    cnHdrFlagsMask                : BYTE   := 16#0F;       // 固定报头标志掩码
    cnHdrDupFlag                  : BYTE   := 16#08;       // DUP 标志位
    cnHdrQoSMask                  : BYTE   := 16#06;       // QoS 标志位掩码
    cnHdrRetainFlag               : BYTE   := 16#01;       // Retain 标志位
    cnConnectFlagUsername         : BYTE   := 16#80;       // CONNECT 用户名标志
    cnConnectFlagPassword         : BYTE   := 16#40;       // CONNECT 密码标志
    cnConnectFlagWillRetain       : BYTE   := 16#20;       // CONNECT 遗嘱保留标志
    cnConnectFlagWillQoS          : BYTE   := 16#18;       // CONNECT 遗嘱 QoS 标志
    cnConnectFlagWill             : BYTE   := 16#04;       // CONNECT 遗嘱使能标志
    cnConnectFlagCleanStart       : BYTE   := 16#02;       // CONNECT 清理会话标志
    cnConnAckSessionPresent       : BYTE   := 16#01;       // CONNACK 会话存在标志

    /// ===================================================================
    /// 默认 KeepAlive、Receive Maximum 与 Packet Identifier 取值范围
    /// ===================================================================
    cnKeepAliveDefault            : UINT   := 60;          // 默认客户端心跳保活周期[s]
    cnKeepAliveFactor             : REAL   := 1.5;         // 心跳超时倍数
    cnDefaultReceiveMax           : UINT   := 65535;       // 默认可并发接收的最大 QoS>0 在途报文数量
    cnMsgIdMin                    : UINT   := 1;           // MQTT Packet Identifier 允许的最小值
    cnMsgIdMax                    : UINT   := 65535;       // MQTT Packet Identifier 允许的最大值

    /// ===================================================================
    /// MQTT 5.0 常用 Reason Code
    /// ===================================================================
    cnRcSuccess                   : BYTE   := 16#00;       // 成功原因码
    cnRcGrantedQoS0               : BYTE   := 16#00;       // 授予 QoS0
    cnRcGrantedQoS1               : BYTE   := 16#01;       // 授予 QoS1
    cnRcGrantedQoS2               : BYTE   := 16#02;       // 授予 QoS2
    cnRcDisconnectWithWill        : BYTE   := 16#04;       // 带遗嘱断开连接
    cnRcNoMatchingSubscribers     : BYTE   := 16#10;       // 无匹配订阅者
    cnRcNoSubscriptionExisted     : BYTE   := 16#11;       // 订阅不存在
    cnRcContinueAuth              : BYTE   := 16#18;       // 继续认证
    cnRcReauth                    : BYTE   := 16#19;       // 重新认证
    cnRcUnspecifiedError          : BYTE   := 16#80;       // 未指定错误
    cnRcMalformedPacket           : BYTE   := 16#81;       // 畸形报文
    cnRcProtocolError             : BYTE   := 16#82;       // 协议错误
    cnRcImplementationSpecific    : BYTE   := 16#83;       // 实现特定错误
    cnRcProtocolUnsupported       : BYTE   := 16#84;       // 协议版本不支持
    cnRcClientIdentifierInvalid   : BYTE   := 16#85;       // 客户端标识符非法
    cnRcBadUsernamePassword       : BYTE   := 16#86;       // 用户名或密码错误
    cnRcNotAuthorized             : BYTE   := 16#87;       // 未授权
    cnRcServerUnavailable         : BYTE   := 16#88;       // 服务器不可用
    cnRcServerBusy                : BYTE   := 16#89;       // 服务器忙
    cnRcBanned                    : BYTE   := 16#8A;       // 被禁止访问
    cnRcServerShuttingDown        : BYTE   := 16#8B;       // 服务器关闭中
    cnRcBadAuthMethod             : BYTE   := 16#8C;       // 认证方法错误
    cnRcKeepAliveTimeout          : BYTE   := 16#8D;       // 心跳超时
    cnRcSessionTakenOver          : BYTE   := 16#8E;       // 会话被接管
    cnRcTopicFilterInvalid        : BYTE   := 16#8F;       // 主题过滤器非法
    cnRcTopicNameInvalid          : BYTE   := 16#90;       // 主题名非法
    cnRcPacketIdInUse             : BYTE   := 16#91;       // 当前 Packet Identifier 已被占用
    cnRcPacketIdNotFound          : BYTE   := 16#92;       // 当前 Packet Identifier 未找到对应上下文
    cnRcReceiveMaxExceeded        : BYTE   := 16#93;       // 对端允许的并发未完成 QoS>0 消息数量已被超出
    cnRcTopicAliasInvalid         : BYTE   := 16#94;       // 主题别名非法
    cnRcPacketTooLarge            : BYTE   := 16#95;       // 报文过大
    cnRcMessageRateTooHigh        : BYTE   := 16#96;       // 消息速率过高
    cnRcQuotaExceeded             : BYTE   := 16#97;       // 超出配额
    cnRcAdministrativeAction      : BYTE   := 16#98;       // 管理操作导致失败
    cnRcPayloadFormatInvalid      : BYTE   := 16#99;       // 载荷格式非法
    cnRcRetainNotSupported        : BYTE   := 16#9A;       // 不支持保留消息
    cnRcQoSNotSupported           : BYTE   := 16#9B;       // 不支持 QoS
    cnRcUseAnotherServer          : BYTE   := 16#9C;       // 需改用其他服务器
    cnRcServerMoved               : BYTE   := 16#9D;       // 服务器已迁移
    cnRcSharedSubNotSupported     : BYTE   := 16#9E;       // 不支持共享订阅
    cnRcConnectionRateExceeded    : BYTE   := 16#9F;       // 连接速率超限
    cnRcMaxConnectTime            : BYTE   := 16#A0;       // 达到最大连接时长
    cnRcSubIdNotSupported         : BYTE   := 16#A1;       // 不支持订阅标识符
    cnRcWildcardSubNotSupported   : BYTE   := 16#A2;       // 不支持通配符订阅

    /// ===================================================================
    /// MQTT 5.0 Property Identifier
    /// ===================================================================
    cnPropPayloadFormat           : BYTE   := 16#01;       // 载荷格式属性
    cnPropMessageExpiry           : BYTE   := 16#02;       // 消息过期属性
    cnPropContentType             : BYTE   := 16#03;       // 内容类型属性
    cnPropResponseTopic           : BYTE   := 16#08;       // 响应主题属性
    cnPropCorrelationData         : BYTE   := 16#09;       // 关联数据属性
    cnPropSubscriptionId          : BYTE   := 16#0B;       // 订阅标识符属性
    cnPropSessionExpiry           : BYTE   := 16#11;       // 会话过期属性
    cnPropAssignedClientId        : BYTE   := 16#12;       // 分配客户端标识符属性
    cnPropServerKeepAlive         : BYTE   := 16#13;       // 服务端心跳属性
    cnPropAuthMethod              : BYTE   := 16#15;       // 认证方法属性
    cnPropAuthData                : BYTE   := 16#16;       // 认证数据属性
    cnPropRequestProblemInfo      : BYTE   := 16#17;       // 请求问题信息属性
    cnPropWillDelayInterval       : BYTE   := 16#18;       // 遗嘱延迟属性
    cnPropRequestResponseInfo     : BYTE   := 16#19;       // 请求响应信息属性
    cnPropResponseInfo            : BYTE   := 16#1A;       // 响应信息属性
    cnPropServerReference         : BYTE   := 16#1C;       // 服务端引用属性
    cnPropReasonString            : BYTE   := 16#1F;       // 原因字符串属性
    cnPropReceiveMaximum          : BYTE   := 16#21;       // Receive Maximum 属性标识符
    cnPropTopicAliasMax           : BYTE   := 16#22;       // 最大主题别名属性
    cnPropTopicAlias              : BYTE   := 16#23;       // 主题别名属性
    cnPropMaxQoS                  : BYTE   := 16#24;       // 最大 QoS 属性
    cnPropRetainAvailable         : BYTE   := 16#25;       // 保留消息可用属性
    cnPropUserProperty            : BYTE   := 16#26;       // 用户属性
    cnPropMaxPacketSize           : BYTE   := 16#27;       // Maximum Packet Size 属性标识符
    cnPropWildcardSubAvail        : BYTE   := 16#28;       // 通配符订阅可用属性
    cnPropSubIdAvail              : BYTE   := 16#29;       // 订阅标识符可用属性
    cnPropSharedSubAvail          : BYTE   := 16#2A;       // 共享订阅可用属性

    /// ===================================================================
    /// 内部属性编解码辅助类型标记
    /// ===================================================================
    cnPropTypeByte                : BYTE   := 1;           // 属性单字节类型
    cnPropTypeUint16              : BYTE   := 2;           // 属性 16 位无符号类型
    cnPropTypeUint32              : BYTE   := 3;           // 属性 32 位无符号类型
    cnPropTypeString              : BYTE   := 4;           // 属性字符串类型
    cnPropTypeBinary              : BYTE   := 5;           // 属性二进制类型
    cnPropTypeStringPair          : BYTE   := 6;           // 属性字符串对类型
END_VAR

完整代码 3:E_MqttConnectFlag.st

这一段完整公开 E_MqttConnectFlag.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttConnectFlag
/// 功能      : MQTT CONNECT 标志位掩码枚举
/// 说明      : 对应 CONNECT 报文可变报头中的 Connect Flags 字节。
///             这些值本质上是位掩码,组包时按需 OR 到同一个字节里。
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttConnectFlag :
(
    CONNECT_FLAG_USERNAME      := 16#80,   // Username Flag,CONNECT 中携带用户名时置位
    CONNECT_FLAG_PASSWORD      := 16#40,   // Password Flag,CONNECT 中携带密码时置位
    CONNECT_FLAG_WILL_RETAIN   := 16#20,   // Will Retain,遗嘱消息按 Retain 方式发布
    CONNECT_FLAG_WILL_QOS1     := 16#08,   // Will QoS 位 0,和 QoS2 掩码组合表示遗嘱 QoS
    CONNECT_FLAG_WILL_QOS2     := 16#10,   // Will QoS 位 1,和 QoS1 掩码组合表示遗嘱 QoS
    CONNECT_FLAG_WILL          := 16#04,   // Will Flag,启用遗嘱消息
    CONNECT_FLAG_CLEAN_START   := 16#02    // Clean Session / Clean Start 标志位
) BYTE;
END_TYPE

完整代码 4:E_MqttInflightState.st

这一段完整公开 E_MqttInflightState.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttInflightState
/// 功能      : MQTT 在途消息状态枚举
/// 说明      : 定义 QoS1 / QoS2 消息在确认流程中的生命周期状态
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttInflightState :
(
    iIdle           := 0,    // 空闲
    iPublishSent    := 1,    // PUBLISH 已发送,等待 PUBACK / PUBREC
    iPubRecReceived := 2,    // 已收到 PUBREC,等待发送 PUBREL
    iPubRelSent     := 3,    // PUBREL 已发送,等待 PUBCOMP
    iCompleted      := 4     // 流程完成
) INT;
END_TYPE

完整代码 5:E_MqttPacketType.st

这一段完整公开 E_MqttPacketType.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttPacketType
/// 功能      : MQTT 控制报文类型枚举
/// 说明      : 定义 MQTT 3.1.1 / 5.0 固定报头高 4 位对应的控制包类型
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-05
/// 版本      : V1.1
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttPacketType :
(
    byReserved            := 16#00,   // 保留值,不允许发送
    byConnect             := 16#10,   // CONNECT 报文
    byConnAck             := 16#20,   // CONNACK 报文
    byPublish             := 16#30,   // PUBLISH 报文(固定头低位另含标志)
    byPubAck              := 16#40,   // PUBACK 报文
    byPubRec              := 16#50,   // PUBREC 报文
    byPubRel              := 16#62,   // PUBREL 报文(含固定标志 0010)
    byPubComp             := 16#70,   // PUBCOMP 报文
    bySubscribe           := 16#82,   // SUBSCRIBE 报文(含固定标志 0010)
    bySubAck              := 16#90,   // SUBACK 报文
    byUnsubscribe         := 16#A2,   // UNSUBSCRIBE 报文(含固定标志 0010)
    byUnsubAck            := 16#B0,   // UNSUBACK 报文
    byPingReq             := 16#C0,   // PINGREQ 报文
    byPingResp            := 16#D0,   // PINGRESP 报文
    byDisconnect          := 16#E0,   // DISCONNECT 报文
    byAuth                := 16#F0    // AUTH 报文(MQTT 5.0)
) BYTE;
END_TYPE

完整代码 6:E_MqttPropertyId.st

这一段完整公开 E_MqttPropertyId.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttPropertyId
/// 功能      : MQTT 5.0 属性标识符枚举
/// 说明      : 定义 MQTT 5.0 各类属性在报文中的标识符编码值,
///             用于 CONNECT / CONNACK / PUBLISH / SUBSCRIBE 等报文属性段编解码
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttPropertyId :
(
    byPropPayloadFormat              := 16#01,   // 载荷格式指示器
    byPropMessageExpiryInterval      := 16#02,   // 消息过期间隔
    byPropContentType                := 16#03,   // 内容类型
    byPropResponseTopic              := 16#08,   // 响应主题
    byPropCorrelationData            := 16#09,   // 关联数据
    byPropSubscriptionId             := 16#0B,   // 订阅标识符
    byPropSessionExpiryInterval      := 16#11,   // Broker 保留断线会话时长属性[s]
    byPropAssignedClientId           := 16#12,   // 分配客户端标识符
    byPropServerKeepAlive            := 16#13,   // 服务端心跳间隔
    byPropAuthMethod                 := 16#15,   // 认证方法
    byPropAuthData                   := 16#16,   // 认证数据
    byPropRequestProblemInfo         := 16#17,   // 请求问题信息
    byPropWillDelayInterval          := 16#18,   // 遗嘱延迟间隔
    byPropRequestResponseInfo        := 16#19,   // 请求响应信息
    byPropResponseInfo               := 16#1A,   // 响应信息
    byPropServerReference            := 16#1C,   // 服务端引用
    byPropReasonString               := 16#1F,   // 原因字符串
    byPropReceiveMax                 := 16#21,   // 对端声明可同时处理的最大 QoS>0 未完成消息数量
    byPropTopicAliasMax              := 16#22,   // 主题别名最大值
    byPropTopicAlias                 := 16#23,   // 主题别名
    byPropMaxQos                     := 16#24,   // 最大 QoS
    byPropRetainAvailable            := 16#25,   // 保留消息可用
    byPropUserProperty               := 16#26,   // 用户属性
    byPropMaxPacketSize              := 16#27,   // 最大报文大小
    byPropWildcardSubAvailable       := 16#28,   // 通配符订阅可用
    byPropSubIdAvailable             := 16#29,   // 订阅标识符可用
    byPropSharedSubscriptionAvailable:= 16#2A    // 共享订阅可用
) BYTE;
END_TYPE

完整代码 7:E_MqttPropertyType.st

这一段完整公开 E_MqttPropertyType.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttPropertyType
/// 功能      : MQTT 5.0 属性值类型枚举
/// 说明      : 定义属性编解码时使用的数据类型分类
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttPropertyType :
(
    byPropTypeByte               := 1,   // 单字节类型
    byPropTypeUint16             := 2,   // 无符号 16 位类型
    byPropTypeUint32             := 3,   // 无符号 32 位类型
    byPropTypeString             := 4,   // 字符串类型
    byPropTypeBinary             := 5,   // 二进制数据类型
    byPropTypeStringPair         := 6    // 字符串对类型
) BYTE;
END_TYPE

完整代码 8:E_MqttQoS.st

这一段完整公开 E_MqttQoS.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttQoS
/// 功能      : MQTT QoS 等级枚举
/// 说明      : 定义 MQTT 消息服务质量级别
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttQoS :
(
    byQoS0            := 0,     // 最多一次,不确认
    byQoS1            := 1,     // 至少一次,需要 PUBACK
    byQoS2            := 2      // 只有一次,四步握手
) BYTE;
END_TYPE

完整代码 9:E_MqttReasonCode.st

这一段完整公开 E_MqttReasonCode.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttReasonCode
/// 功能      : MQTT 5.0 协议原因码枚举
/// 说明      : 定义 MQTT 5.0 报文中的标准原因码,用于 CONNACK、PUBACK、SUBACK、
///             UNSUBACK、DISCONNECT 等报文的原因字段解析与编码
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttReasonCode :
(
    bySuccess                    := 16#00,   // 操作成功或授予 QoS 0
    byGrantedQos1                := 16#01,   // 授予 QoS 1
    byGrantedQos2                := 16#02,   // 授予 QoS 2
    byDisconnectWithWill         := 16#04,   // 带遗嘱断开连接
    byNoMatchingSubscribers      := 16#10,   // 无匹配订阅者
    byNoSubscriptionExisted      := 16#11,   // 订阅不存在
    byContinueAuth               := 16#18,   // 继续认证
    byReauth                     := 16#19,   // 重新认证
    byUnspecifiedError           := 16#80,   // 未指定错误
    byMalformedPacket            := 16#81,   // 畸形报文
    byProtocolError              := 16#82,   // 协议错误
    byImplementationSpecific     := 16#83,   // 实现特定错误
    byUnsupportedProtocolVersion := 16#84,   // 不支持的协议版本
    byClientIdentifierInvalid    := 16#85,   // 客户端标识符非法
    byBadUserNamePassword        := 16#86,   // 用户名或密码错误
    byNotAuthorized              := 16#87,   // 未授权
    byServerUnavailable          := 16#88,   // 服务器不可用
    byServerBusy                 := 16#89,   // 服务器忙
    byBanned                     := 16#8A,   // 被禁止访问
    byServerShuttingDown         := 16#8B,   // 服务器关闭中
    byBadAuthMethod              := 16#8C,   // 认证方法错误
    byKeepAliveTimeout           := 16#8D,   // 心跳超时
    bySessionTakenOver           := 16#8E,   // 会话被接管
    byTopicFilterInvalid         := 16#8F,   // 主题过滤器无效
    byTopicNameInvalid           := 16#90,   // 主题名称无效
    byPacketIdentifierInUse      := 16#91,   // 当前 Packet Identifier 已被占用
    byPacketIdentifierNotFound   := 16#92,   // 当前 Packet Identifier 未找到对应上下文
    byReceiveMaximumExceeded     := 16#93,   // 超过接收最大值
    byTopicAliasInvalid          := 16#94,   // 主题别名无效
    byPacketTooLarge             := 16#95,   // 报文过大
    byMessageRateTooHigh         := 16#96,   // 消息速率过高
    byQuotaExceeded              := 16#97,   // 超过配额
    byAdministrativeAction       := 16#98,   // 管理动作导致失败
    byPayloadFormatInvalid       := 16#99,   // 载荷格式非法
    byRetainNotSupported         := 16#9A,   // 不支持 Retain
    byQosNotSupported            := 16#9B,   // 不支持 QoS
    byUseAnotherServer           := 16#9C,   // 需改用其他服务器
    byServerMoved                := 16#9D,   // 服务器已迁移
    bySharedSubscriptionsNotSupported := 16#9E, // 不支持共享订阅
    byConnectionRateExceeded     := 16#9F,   // 连接速率超限
    byMaximumConnectTime         := 16#A0,   // 达到最大连接时长
    bySubscriptionIdentifiersNotSupported := 16#A1, // 不支持订阅标识符
    byWildcardSubscriptionsNotSupported   := 16#A2, // 不支持通配符订阅
    byMalformedResponse          := 16#FF    // 本地解析用扩展值,非协议标准值
) BYTE;
END_TYPE

完整代码 10:E_MqttState.st

这一段完整公开 E_MqttState.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttState
/// 功能      : MQTT 客户端状态机枚举
/// 说明      : 定义 FB_MqttClient 内部运行状态,用于 TCP 建链、MQTT 握手、
///             Publish / Subscribe 闭环、KeepAlive 与断线恢复流程
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttState :
(
    iDisconnected     := 0,     // 空闲断开状态
    iTcpConnect       := 10,    // TCP 建连中
    iConnect          := 20,    // 发送 CONNECT 报文
    iConnAck          := 30,    // 等待 CONNACK 响应
    iConnected        := 40,    // MQTT 已连接
    iAuth             := 45,    // 认证扩展状态(MQTT 5.0)
    iPublish          := 60,    // 发送 PUBLISH 报文
    iPubAck           := 70,    // 等待 PUBACK
    iPubRec           := 80,    // 等待 PUBREC
    iPubRel           := 90,    // 发送 PUBREL
    iPubComp          := 100,   // 等待 PUBCOMP
    iSubscribe        := 110,   // 发送 SUBSCRIBE 报文
    iSubAck           := 120,   // 等待 SUBACK
    iUnsubscribe      := 130,   // 发送 UNSUBSCRIBE 报文
    iUnsubAck         := 140,   // 等待 UNSUBACK
    iPingReq          := 150,   // 发送心跳请求
    iPingResp         := 160,   // 等待 PINGRESP
    iDisconnect       := 170,   // 发送 MQTT 断开报文
    iTcpDisconnect    := 180    // TCP 断开处理
) INT;
END_TYPE

完整代码 11:E_MqttSubOption.st

这一段完整公开 E_MqttSubOption.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttSubOption
/// 功能      : MQTT 5.0 订阅选项字节枚举
/// 说明      : 定义 SUBSCRIBE 载荷中的订阅选项位编码,
///             用于拼接 QoS、No Local、Retain Handling 等订阅参数
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttSubOption :
(
    bySubOptQos0              := 2#00000000,   // QoS 等级 0
    bySubOptQos1              := 2#00000001,   // QoS 等级 1
    bySubOptQos2              := 2#00000010,   // QoS 等级 2
    bySubOptNoLocal           := 2#00000100,   // 不接收本客户端发布消息
    bySubOptRetainAsPublished := 2#00001000,   // 保留标志按原消息传递
    bySubOptRetainSend        := 2#00000000,   // 发送保留消息
    bySubOptRetainNewOnly     := 2#00010000,   // 仅向新订阅发送保留消息
    bySubOptRetainNotSend     := 2#00100000    // 不发送保留消息
) BYTE;
END_TYPE

完整代码 12:E_MqttVersion.st

这一段完整公开 E_MqttVersion.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_MqttVersion
/// 功能      : MQTT 协议版本枚举
/// 说明      : 用于切换 MQTT 3.1.1 与 MQTT 5.0 编解码逻辑
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_MqttVersion :
(
    byMqttVersion311  := 4,     // MQTT 3.1.1 协议级别
    byMqttVersion50   := 5      // MQTT 5.0 协议级别
) BYTE;
END_TYPE

完整代码 13:E_ReasonCode.st

这一段完整公开 E_ReasonCode.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : E_ReasonCode
/// 功能      : MQTT 客户端内部错误码枚举
/// 说明      : 定义 FB_MqttClient 内部统一错误码,供诊断与状态输出使用
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-05
/// 版本      : V1.1
/// =======================================================================
{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_ReasonCode :
(
    uiErrNoError               := 0,      // 无错误
    uiErrTcpConnectFailed      := 1001,   // TCP 建连失败
    uiErrTcpSendFailed         := 1002,   // TCP 发送失败
    uiErrTcpReceiveFailed      := 1003,   // TCP 接收失败
    uiErrTimeout               := 1004,   // 操作超时
    uiErrProtocolError         := 1005,   // MQTT 协议错误
    uiErrInvalidParameter      := 1006,   // 输入参数非法
    uiErrBufferOverflow        := 1007,   // 缓冲区溢出
    uiErrNotConnected          := 1008,   // 当前未连接
    uiErrSubscriptionFailed    := 1009,   // 订阅失败
    uiErrConnAckRefused        := 1010,   // CONNACK 拒绝连接
    uiErrSubAckRefused         := 1011,   // SUBACK 拒绝订阅
    uiErrPubAckRefused         := 1012,   // PUBACK 返回失败
    uiErrPubRecRefused         := 1013,   // PUBREC 返回失败
    uiErrPubCompRefused        := 1014,   // PUBCOMP 返回失败
    uiErrAuthFailed            := 1015,   // 认证失败
    uiErrSessionExpired        := 1016,   // 会话已过期
    uiErrKeepAliveTimeout      := 1017,   // 心跳超时
    uiErrPacketTooLarge        := 1018,   // 报文过大
    uiErrTopicAliasInvalid     := 1019,   // 主题别名非法
    uiErrReceiveMaxExceeded    := 1020,   // 超过接收上限
    uiErrInvalidState          := 1021    // 状态机状态非法
) UINT;
END_TYPE

完整代码 14:ST_MqttInflight.st

这一段完整公开 ST_MqttInflight.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : ST_MqttInflight
/// 功能      : 在途消息跟踪结构体
/// 说明      : 跟踪 QoS1 / QoS2 消息的重发、状态和超时信息。
///             该结构体偏"通用描述",用于表达一条消息在在途窗口中的完整上下文。
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
TYPE ST_MqttInflight :
STRUCT
    bUsed                  : BOOL;                                  // 当前槽位是否已被一条在途消息占用
    uiMessageId            : UINT;                                  // 该在途消息占用的 Packet Identifier
    eQos                   : E_MqttQoS;                             // 该消息当前使用的 QoS 等级
    sTopic                 : STRING(GVL_Mqtt.cnMaxTopicLen);        // 需要重发时复用的主题
    sPayload               : STRING(GVL_Mqtt.cnMaxPayloadSize);     // 需要重发时复用的载荷
    uiPayloadLen           : UINT;                                  // 发布载荷长度[byte]
    bRetain                : BOOL;                                  // 该消息最初发送时的 Retain 标志
    uiRetryCount           : UINT;                                  // 当前已执行的重试次数
    tElapsed               : TIME;                                  // 距上次发送已累计的时间值[ms]
    bDup                   : BOOL;                                  // 下一次重发时是否需要带 DUP 标志
    eState                 : E_MqttInflightState;                   // 当前在途状态
END_STRUCT
END_TYPE

完整代码 15:ST_MqttInflightMessage.st

这一段完整公开 ST_MqttInflightMessage.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : ST_MqttInflightMessage
/// 功能      : MQTT 出站在途消息结构体
/// 说明      : 用于跟踪 QoS1 / QoS2 发布消息的 Packet Identifier、重试和状态,
///             是 FB_MqttClient 出站在途队列的实际存储结构
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
TYPE ST_MqttInflightMessage :
STRUCT
    bUsed         : BOOL;                                   // 当前条目是否已被有效在途消息占用
    uiPacketId    : UINT;                                   // 该在途消息占用的 Packet Identifier
    eQoS          : E_MqttQoS;                              // 该消息当前使用的 QoS 等级
    sTopic        : STRING(GVL_Mqtt.cnMaxTopicLen);         // 需要重发时复用的发布主题
    sPayload      : STRING(GVL_Mqtt.cnMaxPayloadSize);      // 需要重发时复用的发布载荷
    uiPayloadLen  : UINT;                                   // 发布载荷长度[byte]
    bRetain       : BOOL;                                   // 该消息最初发送时的 Retain 标志
    bDup          : BOOL;                                   // 下一次重发时是否需要带 DUP 标志
    uiRetryCount  : UINT;                                   // 当前已执行的重试次数
    tLastSend     : TIME;                                   // 上次发送时间戳基准[ms]
    eState        : E_MqttInflightState;                    // 当前在途状态
END_STRUCT
END_TYPE

完整代码 16:ST_MqttMessage.st

这一段完整公开 ST_MqttMessage.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : ST_MqttMessage
/// 功能      : MQTT 消息结构体
/// 说明      : 存储应用层消息内容及 MQTT 5.0 扩展属性
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-05
/// 版本      : V1.1
/// =======================================================================
TYPE ST_MqttMessage :
STRUCT
    sTopic               : STRING(256);   // 消息主题
    sPayload             : STRING(2048);  // 消息载荷文本
    uiPayloadLen         : UINT;          // 消息载荷长度[byte]
    eQoS                 : E_MqttQoS;     // 消息 QoS 等级
    bRetain              : BOOL;          // 保留消息标志
    bDup                 : BOOL;          // 重发标志
    uiMessageId          : UINT;          // 该消息关联的 Packet Identifier
    dtTimestamp          : DT;            // 记录该消息的本地时间戳
    byPayloadFormat      : BYTE;          // 载荷格式指示符
    uiMessageExpiry      : UINT;          // 消息在 Broker 中的有效保留时长[s]
    sContentType         : STRING(128);   // 内容类型
    sResponseTopic       : STRING(256);   // 响应主题
    sCorrelationData     : STRING(256);   // 关联数据
    uiTopicAlias         : UINT;          // 主题别名值
    bUseTopicAlias       : BOOL;          // 是否使用主题别名
END_STRUCT
END_TYPE

完整代码 17:ST_MqttProperty.st

这一段完整公开 ST_MqttProperty.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : ST_MqttProperty
/// 功能      : MQTT 5.0 属性结构体
/// 说明      : 保存单个属性的标识符、类型和值
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
TYPE ST_MqttProperty :
STRUCT
    byPropertyIdCode      : BYTE;                 // 属性标识符编码值
    ePropertyType         : E_MqttPropertyType;   // 属性值类型
    byByteValue           : BYTE;                 // 单字节值
    uiUint16Value         : UINT;                 // 16 位无符号值
    udiUint32Value        : UDINT;                // 32 位无符号值
    sStringValue          : STRING(256);          // 字符串值
    uiStringLen           : UINT;                 // 字符串长度
END_STRUCT
END_TYPE

完整代码 18:ST_MqttSubscription.st

这一段完整公开 ST_MqttSubscription.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : ST_MqttSubscription
/// 功能      : MQTT 订阅信息结构体
/// 说明      : 保存订阅主题、状态及 MQTT 5.0 订阅选项
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-05
/// 版本      : V1.1
/// =======================================================================
TYPE ST_MqttSubscription :
STRUCT
    sTopic               : STRING(GVL_Mqtt.cnMaxTopicLen);   // 订阅主题
    eQoS                 : E_MqttQoS;                        // 请求 QoS 等级
    bActive              : BOOL;                             // 订阅是否有效
    bNoLocal             : BOOL;                             // 是否禁止接收本客户端发布消息
    bRetainAsPublished   : BOOL;                             // 是否保持原保留标志
    byRetainHandling     : BYTE;                             // 保留消息处理方式
    udiSubscriptionId    : UDINT;                            // 订阅标识符(MQTT 5.0)
END_STRUCT
END_TYPE

完整代码 19:ST_MqttTopicAlias.st

这一段完整公开 ST_MqttTopicAlias.st。读代码时先看对象职责,再看状态、长度、错误和返回值,不要只抄几行赋值。

iecst 复制代码
/// =======================================================================
/// 名称      : ST_MqttTopicAlias
/// 功能      : MQTT 5.0 主题别名映射结构体
/// 说明      : 保存主题别名和主题名称的连接内映射关系
/// 编程人员  : ControlRookie
/// 时间      : 2026-05-08
/// 版本      : V2.0
/// =======================================================================
TYPE ST_MqttTopicAlias :
STRUCT
    bUsed       : BOOL;                                   // 条目是否有效
    uiAliasId   : UINT;                                   // 别名值
    sTopic      : STRING(GVL_Mqtt.cnMaxTopicLen);         // 主题名称
END_STRUCT
END_TYPE

这一篇你最该记住的几句话

  1. 源码加更不是片段展示,而是完整源码对象公开讲解。
  2. 先建立对象地图,再读状态、报文和事务,现场调试才不会迷路。
  3. 判断源码成熟度,不只看功能是否实现,还要看边界、错误和在线观测量是否闭环。

系列导航

  • 系列定位:MqttClient 系列教程,源码加更阶段,第 11 篇 / 共 16 篇
  • 上一篇:无,先从源码地图开始
  • 下一篇:源码加更02