ARTICLE · 1092954
第26篇_源码加更 08|真机测试、编译入口和 PLC 主程序
适合谁收藏
准备复用本组源码并审查对象边界的工程师。 需要把公开代码装配进目标 CodeSys 工程的人。 准备重新完成编译、离线测试和真机验证的读者。
开源范围 本篇完整公开 4 个 ST 文件。代码直接读取已验证工程,保留声明、实现、注释和缩进;没有伪代码,没有跨文件拼接,也没有省略号。
先给结论
最后一组源码把公共内核、Server、Client、离线测试和真机测试装配到周期任务中,形成可复现工程入口。 本篇的通过标准不是“代码已经贴出”,而是每个文件的职责、调用位置、状态边界和验证入口都能对应起来,并且完整代码可逐字回查源文件。

读图重点
先找到 GVL_HttpRealTest 在本组中的位置,再沿图确认其余对象分别承担数据、状态、执行或诊断职责。图只给阅读顺序,最终判断必须回到下面的完整 ST 代码。
先看文件职责
GVL_HttpRealTest.st | |||
PRG_HttpRealTest.st | |||
PRG_HttpCompileTouch.st | |||
PLC_PRG.st |
阅读顺序不是按文件名机械展开。先看数据和状态,再看公开方法,最后顺着错误出口和调用对象检查边界。源码篇的目标是让读者能对照工程复现,不是用大段代码制造篇幅。
从协议约束到代码职责
协议约束
最后一组源码把公共内核、Server、Client、离线测试和真机测试装配到周期任务中,形成可复现工程入口。 协议层只定义消息与状态成立的条件,工程层还必须把条件分配给确定对象,避免 Parser、Builder、Server、Client 和测试入口互相越权。
对象分工
- 第 1 个对象
GVL_HttpRealTest.st:承担“真机测试参数与观察量”。阅读时先确认输入与公开输出,再追状态、长度和错误出口,最后核对它被谁周期调用或被哪个对象消费。 - 第 2 个对象
PRG_HttpRealTest.st:承担“真机测试执行程序”。阅读时先确认输入与公开输出,再追状态、长度和错误出口,最后核对它被谁周期调用或被哪个对象消费。 - 第 3 个对象
PRG_HttpCompileTouch.st:承担“编译触达入口”。阅读时先确认输入与公开输出,再追状态、长度和错误出口,最后核对它被谁周期调用或被哪个对象消费。 - 第 4 个对象
PLC_PRG.st:承担“PLC 周期装配入口”。阅读时先确认输入与公开输出,再追状态、长度和错误出口,最后核对它被谁周期调用或被哪个对象消费。
装配与验证
这组文件必须作为一个职责单元阅读和编译。先用确定输入验证“真机参数集中管理”,再制造非法或容量边界验证“编译触达防止对象未被工程引用”,最后在真实通信或上层调用中确认“PLC_PRG 是运行装配点而不是业务大杂烩”。如果单文件测试通过但装配后失败,应优先检查结构体、常量、状态枚举和周期调用关系,而不是立即重写核心算法。源码完整公开只证明读者拿到了同一事实源,目标运行时是否通过仍需重新编译和真机取证。
本篇从 GVL_HttpRealTest.st 开始审查:先确认“真机参数集中管理”对应的类型、常量或公开输入,再沿 CASE 或方法调用追踪成功路径,随后逐个检查容量、超时和协议错误出口,最后在 PLC_PRG.st 对应的结果或装配位置确认错误能被观测、清理并再次执行。这个顺序专门用来区分“算法正确但没有周期调用”“状态能完成但错误被覆盖”和“PLC_PRG 是运行装配点而不是业务大杂烩尚未形成验证证据”三类问题。只有本篇源码、装配和验证使用同一组对象与同一组边界,完整开源才具有可复现意义。
本篇完整开源代码
下面按职责顺序给出本篇全部 ST 文件。每个代码块保留完整声明与实现;阅读时把状态、长度、错误出口和调用对象与上面的职责表逐项对照。
完整源码 1:GVL_HttpRealTest.st
职责:真机测试参数与观察量
/// 功能 : HTTP 真机联调控制和结果变量。/// 库依赖 : CAA Net Base Services{attribute 'qualified_only'}VAR_GLOBALbResetResults : BOOL := FALSE; // 一次性复位真机联调结果和命令位。udiNowMs : UDINT := 0; // 示例任务毫秒时钟,按扫描周期累加。bServerEnable : BOOL := TRUE; // Server 使能,下载后默认监听,便于通信猫直接访问 PLC。bServerCloseConnection : BOOL := TRUE; // Server 响应后主动关闭连接,匹配通信猫短连接调试习惯。sServerBindIP : STRING := '0.0.0.0'; // Server 绑定 IP,0.0.0.0 表示任意网卡。uiServerPort : UINT := GVL_Http.cnDefaultServerPort; // Server 监听端口,默认 18088,匹配通信猫访问 PLC 的 URL。uiServerResponseStatusCode : UINT := 200; // Server 自定义响应状态码,内置路由默认 200。sServerResponseBody : STRING(GVL_Http.cnMaxBodySize) := 'hello from PLC Server';// Server 自定义响应 body,非空时未知路径也返回 200。sServerResponseContentType : STRING(96) := GVL_Http.cnDefaultContentType; // Server 自定义响应 Content-Type。sServerAdditionalHeader : STRING(GVL_Http.cnMaxHeaderSize) := ''; // Server 自定义响应 Header 行。bServerListening : BOOL := FALSE; // Server 已获得监听句柄。bServerRunning : BOOL := FALSE; // Server 正在运行。bServerBusy : BOOL := FALSE; // Server 正在初始化或等待监听。bServerError : BOOL := FALSE; // Server 错误锁存。diServerErrorID : DINT := 0; // Server 错误诊断码。sServerDiagMsg : STRING(255) := ''; // Server 诊断文本。eServerState : E_HttpServerState := E_HttpServerState.iDisabled; // Server 状态机。eServerLastError : E_HttpError := E_HttpError.iNoError; // Server 最近 HTTP 错误。eServerLastNbsError : NBS.ERROR; // Server 最近 NBS 错误。uiServerActiveConnections : UINT := 0; // Server 当前活跃连接数。uiServerLastRequestSlot : UINT := 0; // Server 最近请求槽位。uiServerLastErrorSlot : UINT := 0; // Server 最近错误槽位。sServerLastTarget : STRING(GVL_Http.cnMaxTargetLen) := ''; // Server 最近请求路径。sServerLastBody : STRING(GVL_Http.cnMaxBodySize) := ''; // Server 最近请求 body。sServerRxMessage : STRING(GVL_Http.cnMaxMessageSize) := ''; // Server 最近一次接收报文。sServerTxMessage : STRING(GVL_Http.cnMaxMessageSize) := ''; // Server 最近一次发送报文。udiServerAcceptedCount : UDINT := 0; // Server 累计接入连接数。udiServerRequestCount : UDINT := 0; // Server 累计请求数。udiServerResponseCount : UDINT := 0; // Server 累计响应数。udiServerProtocolErrorCount : UDINT := 0; // Server 累计协议错误数。hServerListenHandle : NBS.CAA.HANDLE; // Server 监听句柄快照。bServerPassLatched : BOOL := FALSE; // Server 外部 client 联调通过锁存。bClientEnable : BOOL := TRUE; // Client 使能,下载后可直接向通信猫发起 HTTP 请求。bClientSend : BOOL := TRUE; // Client 下载后默认发送一次,PLC_PRG 读取后自复位。bClientAbort : BOOL := FALSE; // Client 中止当前连接命令。bClientCloseConnection : BOOL := TRUE; // Client 每次响应后主动关闭连接,优先保证通信猫短连接调试稳定。udiClientTimeoutUs : UDINT := 10000000; // Client 请求超时,单位 [us],通信猫联调默认 10 s。sClientURL : STRING(1024) := ''; // Client URL 留空时使用下方 IP/Host/Path 分离引脚。sClientServerIP : STRING := '192.168.20.2'; // Client 远端 HTTP Server IP。uiClientPort : UINT := 6971; // Client 远端 HTTP Server 端口,匹配通信猫 HTTP 服务端口。sClientHost : STRING(GVL_Http.cnMaxHostLen) := '192.168.20.2:6971'; // Client Host 字段,匹配通信猫监听端口。sClientPath : STRING(GVL_Http.cnMaxTargetLen) := '/api/tongxinmao'; // Client 请求路径,匹配通信猫 URL。eClientMethod : E_HttpMethod := E_HttpMethod.iPost; // Client 请求方法,默认 POST 便于发送正文。sClientBody : STRING(GVL_Http.cnMaxBodySize) := 'hello from PLC'; // Client 请求 body,通信猫接收区可直接看到。sClientContentType : STRING(96) := 'text/plain'; // Client Content-Type,匹配通信猫普通文本联调。sClientAdditionalHeader : STRING(GVL_Http.cnMaxHeaderSize) := ''; // Client 自定义 Header 行。bClientTcpConnected : BOOL := FALSE; // Client TCP 连接快照。bClientBusy : BOOL := FALSE; // Client 正在执行事务。bClientDone : BOOL := FALSE; // Client 本次事务完成。bClientError : BOOL := FALSE; // Client 错误锁存。diClientErrorID : DINT := 0; // Client 错误诊断码。sClientDiagMsg : STRING(255) := ''; // Client 诊断文本。eClientState : E_HttpClientState := E_HttpClientState.iDisabled; // Client 状态机。eClientLastError : E_HttpError := E_HttpError.iNoError; // Client 最近 HTTP 错误。eClientLastNbsError : NBS.ERROR; // Client 最近 NBS 错误。uiClientStatusCode : UINT := 0; // Client 最近 HTTP 响应状态码。sClientReason : STRING(64) := ''; // Client 最近 HTTP 响应原因短语。sClientResponseBody : STRING(GVL_Http.cnMaxBodySize) := ''; // Client 最近 HTTP 响应 body。sClientResponseHeader : STRING(GVL_Http.cnMaxHeaderSize) := ''; // Client 最近 HTTP 响应 Header。sClientTxMessage : STRING(GVL_Http.cnMaxMessageSize) := ''; // Client 最近一次发送报文。sClientRxMessage : STRING(GVL_Http.cnMaxMessageSize) := ''; // Client 最近一次接收报文。udiClientRequestCount : UDINT := 0; // Client 累计请求数。udiClientResponseCount : UDINT := 0; // Client 累计响应数。udiClientConnectErrorCount : UDINT := 0; // Client 累计连接错误数。udiClientProtocolErrorCount : UDINT := 0; // Client 累计协议错误数。bClientPassLatched : BOOL := FALSE; // Client 外部 server 联调通过锁存。bOverallPassLatched : BOOL := FALSE; // Server 与 Client 双向联调均通过锁存。bClientMatrixAutoEnable : BOOL := FALSE; // 真机矩阵自动运行开关,发布默认关闭,避免下载后自动访问外部网络。bClientMatrixRun : BOOL := FALSE; // 真机矩阵运行命令,由自动开关或在线命令置位。bClientMatrixDone : BOOL := FALSE; // 真机矩阵完成标志。bClientMatrixPassed : BOOL := FALSE; // 真机矩阵全部通过标志。bClientMatrixError : BOOL := FALSE; // 真机矩阵存在失败用例标志。uiClientMatrixTotal : UINT := 15; // 真机矩阵用例总数。uiClientMatrixPassed : UINT := 0; // 真机矩阵通过用例数。uiClientMatrixFailed : UINT := 0; // 真机矩阵失败用例数。uiClientMatrixCase : UINT := 0; // 真机矩阵当前用例序号。uiClientMatrixStep : UINT := 0; // 真机矩阵内部步骤快照。uiClientMatrixLastStatus : UINT := 0; // 最近一次 Client 响应状态码。eClientMatrixLastError : E_HttpError := E_HttpError.iNoError; // 最近一次 Client 协议栈错误码。sClientMatrixCaseID : STRING(48) := ''; // 当前真机矩阵用例 ID。sClientMatrixFailedCase : STRING(48) := ''; // 首个失败用例 ID。sClientMatrixDiagMsg : STRING(255) := ''; // 真机矩阵诊断文本。END_VAR
完整源码 2:PRG_HttpRealTest.st
职责:真机测试执行程序
/// 功能 : HTTP 真机联调旧入口兼容占位。/// 库依赖 : CAA Net Base ServicesPROGRAM PRG_HttpRealTestVAREND_VAR// === IMPLEMENTATION ===/// =======================================================================/// 名称 : PRG_HttpRealTest/// 功能 : 保留旧工程树对象名称,真实 Server/Client 联调入口已统一到 PLC_PRG。/// 库依赖 : CAA Net Base Services/// =======================================================================/// 使用说明 : 1. 本程序不再实例化 HTTP 功能块,不再处理发送、复位或中止命令。/// : 2. 在线联调请直接打开 PLC_PRG,操作 GVL_HttpRealTest 中的全局变量。/// =======================================================================// 兼容原因:当前 SmartControl CLI 删除 IEC 对象能力受限,保留空 PRG 可避免旧入口继续干扰在线联调。
完整源码 3:PRG_HttpCompileTouch.st
职责:编译触达入口
/// 功能 : HTTP 编译触达旧入口兼容占位。/// 库依赖 : CAA Net Base ServicesPROGRAM PRG_HttpCompileTouchVAREND_VAR// === IMPLEMENTATION ===/// =======================================================================/// 名称 : PRG_HttpCompileTouch/// 功能 : 保留旧工程树对象名称,当前 HTTP 对象由 PLC_PRG 中的完整功能块调用覆盖。/// 库依赖 : CAA Net Base Services/// =======================================================================/// 使用说明 : 1. 本程序不再实例化 Client、Server 或连接槽位功能块。/// : 2. 编译与联调以 PLC_PRG 为唯一用户入口。/// =======================================================================// 兼容原因:当前 SmartControl CLI 删除 IEC 对象能力受限,空 PRG 仅用于保持工程树兼容。
完整源码 4:PLC_PRG.st
职责:PLC 周期装配入口
/// 功能 : HTTP 示例工程任务入口。/// 库依赖 : CAA Net Base ServicesPROGRAM PLC_PRGVARfbHttpClient : FB_HttpClient; // HTTP Client 主实例。fbHttpServer : FB_HttpServer; // HTTP Server 主实例。stClientMetrics : ST_HttpClientMetrics; // Client 指标输出。stServerMetrics : ST_HttpServerMetrics; // Server 指标输出。stClientResponse : ST_HttpResponse; // Client 响应输出。aServerSnapshots : ARRAY[1..GVL_Http.cnMaxClientSlots] OF ST_HttpConnectionSnapshot; // Server 连接快照。END_VAR// === IMPLEMENTATION ===/// =======================================================================/// 名称 : PLC_PRG/// 功能 : HTTP 示例工程主任务,直接完整调用 Client 和 Server 功能块。/// 库依赖 : CAA Net Base Services/// =======================================================================/// 使用说明 : 1. Client/Server 所有真实网络命令默认关闭,由 GVL_HttpRealTest 在线变量触发。/// : 2. 本主程序完整列出功能块引脚,便于 CODESYS/SmartControl 用户按引脚理解用法。/// =======================================================================// 工程说明:本段集中处理状态、边界或诊断,避免跨周期残留。// 约束:PLC_PRG 作为示例主入口直接调用 HTTP 功能块;用户联调不再依赖辅助 PRG。GVL_HttpRealTest.udiNowMs := GVL_HttpRealTest.udiNowMs + 10;IF GVL_HttpRealTest.bResetResults THENfbHttpServer.M_Reset();fbHttpClient.M_Reset();GVL_HttpRealTest.bClientSend := FALSE;GVL_HttpRealTest.bClientAbort := FALSE;GVL_HttpRealTest.bServerPassLatched := FALSE;GVL_HttpRealTest.bClientPassLatched := FALSE;GVL_HttpRealTest.bOverallPassLatched := FALSE;END_IFfbHttpServer(xEnable := GVL_HttpRealTest.bServerEnable,xReset := GVL_HttpRealTest.bResetResults,xCloseConnection := GVL_HttpRealTest.bServerCloseConnection,bEnable := GVL_HttpRealTest.bServerEnable,sBindIP := GVL_HttpRealTest.sServerBindIP,uiPort := GVL_HttpRealTest.uiServerPort,uiResponseStatusCode := GVL_HttpRealTest.uiServerResponseStatusCode,sResponseBody := GVL_HttpRealTest.sServerResponseBody,sResponseContentType := GVL_HttpRealTest.sServerResponseContentType,sAdditionalHeader := GVL_HttpRealTest.sServerAdditionalHeader,udiNowMs := GVL_HttpRealTest.udiNowMs,xListening => GVL_HttpRealTest.bServerListening,xRunning => GVL_HttpRealTest.bServerRunning,xBusy => GVL_HttpRealTest.bServerBusy,xError => GVL_HttpRealTest.bServerError,bListening => GVL_HttpRealTest.bServerListening,bRunning => GVL_HttpRealTest.bServerRunning,bBusy => GVL_HttpRealTest.bServerBusy,bError => GVL_HttpRealTest.bServerError,diErrorID => GVL_HttpRealTest.diServerErrorID,sDiagMsg => GVL_HttpRealTest.sServerDiagMsg,eLastNbsError => GVL_HttpRealTest.eServerLastNbsError,eState => GVL_HttpRealTest.eServerState,eLastError => GVL_HttpRealTest.eServerLastError,stMetrics => stServerMetrics,uiActiveConnections => GVL_HttpRealTest.uiServerActiveConnections,uiLastErrorSlot => GVL_HttpRealTest.uiServerLastErrorSlot,uiLastRequestSlot => GVL_HttpRealTest.uiServerLastRequestSlot,sRequestTarget => GVL_HttpRealTest.sServerLastTarget,sRequestBody => GVL_HttpRealTest.sServerLastBody,sRxMessage => GVL_HttpRealTest.sServerRxMessage,sTxMessage => GVL_HttpRealTest.sServerTxMessage,sLastTarget => GVL_HttpRealTest.sServerLastTarget,sLastBody => GVL_HttpRealTest.sServerLastBody,hListenHandle => GVL_HttpRealTest.hServerListenHandle,aConnectionSnapshots => aServerSnapshots);fbHttpClient(xEnable := GVL_HttpRealTest.bClientEnable,xExecute := GVL_HttpRealTest.bClientSend,xReset := GVL_HttpRealTest.bResetResults,xAbort := GVL_HttpRealTest.bClientAbort,xCloseConnection := GVL_HttpRealTest.bClientCloseConnection,bEnable := GVL_HttpRealTest.bClientEnable,bSend := GVL_HttpRealTest.bClientSend,udiTimeOut := GVL_HttpRealTest.udiClientTimeoutUs,sURL := GVL_HttpRealTest.sClientURL,sServerIP := GVL_HttpRealTest.sClientServerIP,uiPort := GVL_HttpRealTest.uiClientPort,sHost := GVL_HttpRealTest.sClientHost,sPath := GVL_HttpRealTest.sClientPath,eRequestType := GVL_HttpRealTest.eClientMethod,eMethod := GVL_HttpRealTest.eClientMethod,sBody := GVL_HttpRealTest.sClientBody,sContentType := GVL_HttpRealTest.sClientContentType,sAdditionalHeader := GVL_HttpRealTest.sClientAdditionalHeader,udiNowMs := GVL_HttpRealTest.udiNowMs,xActive => GVL_HttpRealTest.bClientTcpConnected,xConnected => GVL_HttpRealTest.bClientTcpConnected,xBusy => GVL_HttpRealTest.bClientBusy,xDone => GVL_HttpRealTest.bClientDone,xError => GVL_HttpRealTest.bClientError,bTcpConnected => GVL_HttpRealTest.bClientTcpConnected,bBusy => GVL_HttpRealTest.bClientBusy,bDone => GVL_HttpRealTest.bClientDone,bError => GVL_HttpRealTest.bClientError,diErrorID => GVL_HttpRealTest.diClientErrorID,sDiagMsg => GVL_HttpRealTest.sClientDiagMsg,eLastNbsError => GVL_HttpRealTest.eClientLastNbsError,eState => GVL_HttpRealTest.eClientState,eLastError => GVL_HttpRealTest.eClientLastError,uiStatusCode => GVL_HttpRealTest.uiClientStatusCode,sTxMessage => GVL_HttpRealTest.sClientTxMessage,sRxMessage => GVL_HttpRealTest.sClientRxMessage,sResponseBody => GVL_HttpRealTest.sClientResponseBody,sResponseHeader => GVL_HttpRealTest.sClientResponseHeader,stMetrics => stClientMetrics,stResponse => stClientResponse);IF GVL_HttpRealTest.bClientSend THENGVL_HttpRealTest.bClientSend := FALSE;END_IFIF GVL_HttpRealTest.bClientAbort THENGVL_HttpRealTest.bClientAbort := FALSE;END_IFIF GVL_HttpRealTest.bResetResults THENGVL_HttpRealTest.bResetResults := FALSE;END_IFGVL_HttpRealTest.udiServerAcceptedCount := stServerMetrics.udiAcceptedConnections;GVL_HttpRealTest.udiServerRequestCount := stServerMetrics.udiRequestCount;GVL_HttpRealTest.udiServerResponseCount := stServerMetrics.udiResponseCount;GVL_HttpRealTest.udiServerProtocolErrorCount := stServerMetrics.udiProtocolErrorCount;GVL_HttpRealTest.udiClientRequestCount := stClientMetrics.udiRequestCount;GVL_HttpRealTest.udiClientResponseCount := stClientMetrics.udiResponseCount;GVL_HttpRealTest.udiClientConnectErrorCount := stClientMetrics.udiConnectErrorCount;GVL_HttpRealTest.udiClientProtocolErrorCount := stClientMetrics.udiProtocolErrorCount;GVL_HttpRealTest.sClientReason := stClientResponse.sReason;IF (GVL_HttpRealTest.udiServerRequestCount > 0)AND (GVL_HttpRealTest.udiServerResponseCount > 0)AND NOT GVL_HttpRealTest.bServerError THENGVL_HttpRealTest.bServerPassLatched := TRUE;END_IFIF GVL_HttpRealTest.bClientDoneAND NOT GVL_HttpRealTest.bClientErrorAND (GVL_HttpRealTest.uiClientStatusCode >= 200)AND (GVL_HttpRealTest.uiClientStatusCode < 300) THENGVL_HttpRealTest.bClientPassLatched := TRUE;END_IFGVL_HttpRealTest.bOverallPassLatched := GVL_HttpRealTest.bServerPassLatchedAND GVL_HttpRealTest.bClientPassLatched;
本篇阅读抓手
真机参数集中管理。 编译触达防止对象未被工程引用。 PLC_PRG 是运行装配点而不是业务大杂烩。
如何验证这组源码
先针对 GVL_HttpRealTest.st的公开输入和错误出口建立确定性用例。再把“真机参数集中管理”转成至少一个正常场景和一个失败场景。 本篇 4 个文件必须一起编译,避免只验证单个函数而漏掉数据结构或调用边界。 真机复核时重点观察“PLC_PRG 是运行装配点而不是业务大杂烩”,并保留对应状态、计数和原始报文。
这一篇你最该记住
真机参数集中管理。 编译触达防止对象未被工程引用。 PLC_PRG 是运行装配点而不是业务大杂烩。
系列导航
系列:CodeSys HTTP 系列教程,第 26/28 篇。 当前源码加更:第 8/8 篇。 本篇完整源码文件数:4。 上一篇:第25篇 下一篇:第27篇