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

大众VASS06标准:FB 193 FB_Diagnose_SNMP解读,通过SNMP诊断网络设备

07/30 10:45
1167
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

功能块概述

FB_Diagnose_SNMP 是一个用于通过 SNMP 协议诊断网络设备的功能块,版本为 1.1.0。它可以扫描和监控网络中的设备状态,提供设备健康诊断信息。

wincc面板:

主要作用

这个功能块的核心作用是:

设备发现与监控:通过 SNMP 协议扫描网络中的设备(最多512台)

状态诊断:收集每个设备的关键运行信息

故障检测:监控网络流量异常,识别潜在问题

可视化支持:为 HMI 界面提供诊断数据显示

收集的设备信息

该功能块通过 SNMP-GET 命令获取以下设备信息:IP 地址系统描述系统运行时间(自上次重启后的时间)

活跃网络套接字数量端口数据:入站/出站丢弃报文数量入站/出站错误报文数量

设备状态分类

功能块将设备状态分为以下几类:

状态值 含义 显示颜色
0 设备无法通过 SNMP 访问 灰色
1 设备正常 绿色
2 设备达到警告阈值 黄色
4 设备达到错误阈值 红色
8 设备未检测到(之前状态正常) 深绿色
16 设备未检测到(之前状态警告) 深黄色
24 设备未检测到(之前状态错误) 深红色

如何使用该功能块

1. 基本配置

调用位置:FC11 (Grundfunktionen)
实例数据块:x#FB_Diagnose_SNMP_DB(例如:1#FB_Diagnose_SNMP_DB)
符号注释:Diagnóstico SNMP

2. 输入参数配置

oktett3Subnet2 (INT):第二个子网的第三个八位字节

diff_warn:警告阈值(报文数量变化)

diff_err:错误阈值(报文数量变化)

cfg (WORD):配置位

      • Bit 0 = 0:扫描256台设备Bit 0 = 1:扫描512台设备

VerbID_IP_V4TCP/IP 连接参数ZUeLesen (Time):SNMP 响应等待超时时间

3. 输出数据

Geraete 数组:包含所有设备的诊断数据

Status 数组:每个设备的状态码

Dialog_Device:选中设备的详细信息

4. 依赖项

使用前需要确保以下库和块可用:LSNMP_Get 相关功能块FB_PNIO_Port_Diag各种字符串处理功能块相关的 UDT 数据类型

5. 实际应用步骤

初始化:在项目中添加功能块实例

参数设置:配置网络参数和阈值

周期调用:在 FC11 中周期性调用该功能块

数据监控:通过 HMI 界面监控设备状态

故障处理:根据状态码识别和处理网络问题

6. 监控原理

该功能块通过比较当前扫描与上次扫描的报文统计数据来判断设备状态:

如果丢弃/错误报文增加量超过 diff_warn,设置警告状态如果增加量超过 diff_err,设置错误状态这种方式可以有效检测网络通信质量问题,帮助维护人员及时发现和解决网络故障。

功能块深度解读:

整体说明

该功能块(FB)的核心功能是通过SNMP 协议Profinet 诊断(PNIO) 实现对网络中设备(如 PLC、交换机等)的状态监控与诊断,包括设备 IP 地址、运行时间、端口错误 / 丢弃数据包数量等,并通过 HMI 界面展示状态、接收用户操作(如刷新、复位)。

1. 版本与版权声明(Aenderungsjournal)

REGION (/* Aenderungsjournal */)(*********************************************************************************************  Copyright  2016             SIEMENS AG D-90475 Nuernberg                            ****  All Rights Reserved         VOLKSWAGEN AG, 38436 Wolfsburg                          ****                              AUDI AG, D-85045 Ingolstadt                             ********************************************************************************************    Datum       Version     Autor       Beschreibung                                    ------------------------------------------------------------------------------------------    27.09.22    1.5.00      Lohrenz     OID Gerätebeschreibung und Laufzeit von GetNext                                        auf Get geändert.    28.04.22    1.4.00      Hennig      Werte Meldungen als Input    16.02.22    1.1.00      Lohrenz     Neuerstellung     16.02.22    1.0.01      Lohrenz     Optimierung Datenstruktur HMI-UDT                                        HMI Bedienkonzept ueberarbeitet.                                        VerbID_IP_V4 zur parametrierung von ID und                                         LocalPort hinzugefuegt.                                        Antwortzeit über ZUeLesen hinzugefuegt.                                        Mehrsprachigkeit vorbereitet      28.03.22    V1.0.02     Lohrenz     Dialog Refresh führt SNMP- und                                        Profinetdaten-Aktualsierung durch.    01.04.22    V1.0.03     Lohrenz     Korrektur bei 2 Profinetkreisen bei Refresh*****************************************************************************************)END_REGION

作用:记录功能块的版本迭代、作者及修改内容,便于追溯代码变更。

关键信息:版权归属西门子、大众、奥迪,说明该功能块用于汽车行业相关设备诊断。核心修改包括:优化 HMI 数据结构、增加 IP 参数配置、支持 SNMP/Profinet 刷新、修正 Profinet 环路问题等,体现功能块从创建到完善的过程。

2. 功能描述(Funktionsberschreibung)

REGION Funktionsberschreibung    (/* In diesem FB wird die Diagnose von Teilnehmer mittels SNMP und RDREC realisiert.        Werte fuer Warnungsmeldung und Störungsmeldung noch in Erprobung */)END_REGION

作用:简要说明功能块的核心用途。

解读:通过SNMP(简单网络管理协议) 和RDREC(可能是 Profinet 相关的诊断函数)

    •  实现对网络中 “参与者”(设备)的诊断;警告和故障阈值仍在测试中,尚未定型。

3. 配置位(Konfigurationsbits)

REGION Konfigurationsbits (* BIT      KommentarX0       0 = 256 Teilnehmer 1 = 512 TeilnehmerX1       ReserveX2       ReserveX3       ReserveX4       ReserveX5       ReserveX6       ReserveX7       ReserveX8       ReserveX9       ReserveX10      ReserveX11      ReserveX12      ReserveX13      ReserveX14      ReserveX15      Reserve*)END_REGION

作用:定义功能块的可配置参数,通过位操作调整运行模式。

解读

X0位:控制可监控的设备数量 ——0 对应 256 台,1 对应 512 台,适应不同规模的网络。

X1~X15:预留位,为未来扩展功能保留(如增加更多设备、支持新协议等)。

4. 可视化按钮(VISUTASTEN)

该区域处理HMI 界面按钮的输入信号,将按钮操作转换为功能块内部的控制信号,并进行边沿检测(避免持续触发)。

REGION (/*VISUTASTEN*/)    (/*Tasten von Visu auswerten*/)    IF "DB_ARG".PC_AKTIV = DWORD#16#00000000 THEN        #"HMI-UDT".bVisuTasten := Byte#0;  // 若PC未激活,重置可视化按钮值    END_IF;
    // 按钮值映射:将HMI的bVisuTasten(字节)与具体操作绑定    #xSF_Reset_Detail_Alle_Geraete := (#"HMI-UDT".bVisuTasten = Byte#1);  // 复位所有设备详情    #xSF_Reset_Detail_Einzel_Geraet := (#"HMI-UDT".bVisuTasten = Byte#2);  // 复位单个设备详情    #xSF_EbeneZurueck_Detail := (#"HMI-UDT".bVisuTasten = Byte#3);  // 返回上一级界面    #xSF_EbeneVor_Detail := (#"HMI-UDT".bVisuTasten = Byte#4);  // 进入下一级界面    #xSF_Button_Dialog_Ports_1_8 := (#"HMI-UDT".bVisuTasten = Byte#10);  // 显示端口1-8    #xSF_Button_Dialog_Ports_9_16 := (#"HMI-UDT".bVisuTasten = Byte#11);  // 显示端口9-16    #xSF_Button_Dialog_Ports_17_24:= (#"HMI-UDT".bVisuTasten = Byte#12);  // 显示端口17-24    #xSF_Button_Dialog_Ports_25_32 := (#"HMI-UDT".bVisuTasten = Byte#13);  // 显示端口25-32    #xSF_Button_Dialog_Refresh := (#"HMI-UDT".bVisuTasten = Byte#14);  // 刷新数据
    (/*Flankenauswertung 边沿检测:确保按钮按下仅触发一次*/)    #xR_Trig_Button_Detail_EbeneZurueck.Q := #xSF_EbeneZurueck_Detail AND NOT #xR_Trig_Button_Detail_EbeneZurueck.Edge;    #xR_Trig_Button_Detail_EbeneZurueck.Edge := #xSF_EbeneZurueck_Detail;    // (其他按钮的边沿检测逻辑相同,此处省略)
    // 刷新按钮按下时,设置标志位(避免重复触发)    IF #xR_Trig_Button_Dialog_Refresh.Q AND NOT #xButton_Dialog_Refresh_Pressed THEN        #xButton_Dialog_Refresh_Pressed := TRUE;    END_IF;END_REGION

核心逻辑

按钮值映射:HMI 界面的按钮操作通过"HMI-UDT".bVisuTasten(字节变量)传递,不同字节值对应不同操作(如复位、切换端口页、刷新等)。

边沿检测:通过xR_Trig_*变量检测按钮的 “上升沿”(从 0 到 1 的跳变),确保一次按钮按下仅触发一次操作(避免按钮持续按下导致的重复执行)。

刷新标志xButton_Dialog_Refresh_Pressed用于标记 “刷新” 操作已触发,后续流程会根据该标志执行数据更新。

5. 主流程(Ablauf)

该区域是功能块的核心,包含初始化、设备扫描、SNMP/Profinet 数据获取、状态判断、HMI 交互等逻辑。

5.1 初始化控制
REGION (/*Ablauf*/)    IF "DB_ARG".NEUSTART_STEU_EIN THEN  // 若收到重启控制信号        #xinit := false;  // 重置初始化标志,触发重新初始化    END_IF;

作用:通过外部信号(NEUSTART_STEU_EIN)控制初始化状态,确保设备重启后重新加载配置。

5.2 读取 PLC 自身 IP 地址(SPS IP-Adresse lesen)
REGION (/*SPS IP-Adresse lesen*/)    (/*CPU Geräteadresse是10(VASS标准规定第4字节为10),但实际系统中CPU地址为0,此为刻意设计*/)    IF NOT #xinit THEN  // 未初始化时执行        #FB_PNIO_Port_Diag_Instance(REQ := TRUE, iMaster := 100, iDevice := 0);  // 调用Profinet诊断功能块
        IF #FB_PNIO_Port_Diag_Instance.DONE THEN  // 诊断完成            // 存储PLC的IP地址到设备数组(索引10,对应PLC)            #Geraete[10].IPAdress[0] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[0];            #Geraete[10].IPAdress[1] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[1];            #Geraete[10].IPAdress[2] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[2];            #bPLCOktett3 := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[2];  // 保存IP第3字节            #Geraete[10].IPAdress[3] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[3];            #DeviceIP_PLC := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[3];  // PLC的IP第4字节
            // 设置PLC的系统描述(固定为"PLC")            #Geraete[10].SysDescription := 'PLC';
            // 计算PLC端口的总丢弃/错误数据包(累加端口0和1的数据)            #Geraete[10].Ports.ifDiscards := #FB_PNIO_Port_Diag_Instance.PortData.Ports[0].InDiscard + ...;            #Geraete[10].Ports.ifError := #FB_PNIO_Port_Diag_Instance.PortData.Ports[0].InError + ...;
            // 根据错误/丢弃量与上次的差值判断状态            IF (错误差 > 故障阈值) OR (丢弃差 > 故障阈值) THEN                #Geraete[10].Status := 4;  // 故障状态            ELSIF (错误差 > 警告阈值) OR (丢弃差 > 警告阈值) THEN                #Geraete[10].Status := 2;  // 警告状态            ELSE                #Geraete[10].Status := 1;  // 正常状态            END_IF;
            #arrStatus[10] := INT_TO_BYTE(IN := #Geraete[10].Status);  // 保存状态到数组            #iPortAnzeigeOffset := 1;  // 端口显示偏移量初始化为1            #"HMI-UDT".bRueckmeldungTasten := 10;  // 向HMI反馈初始化完成            #xinit := true;  // 标记初始化完成        END_IF;    END_IF;END_REGION

核心逻辑:调用FB_PNIO_Port_Diag_Instance(Profinet 端口诊断功能块)获取 PLC 自身的 IP 地址和端口数据。将 PLC 信息存储到设备数组Geraete[10]

      • (索引 10 是 VASS 标准规定的 PLC 地址)。根据端口错误 / 丢弃数据包的增量(与上次值的差值)判断 PLC 状态(正常 = 1、警告 = 2、故障 = 4),并同步到 HMI。
5.3 设备扫描控制(Scanner)
REGION (/*Scanner*/)    // 确保iDevice(设备索引)在有效范围    IF #iDevice < 1 THEN  // 最小值为1        #iDevice := 1;    ELSIF (#iDevice > 512 AND #cfg.%X0) OR (#iDevice > 256 AND NOT #cfg.%X0) THEN        #iDevice := 1;  // 超过最大设备数(512或256)时重置为1    ELSIF #iDevice = #DeviceIP_PLC THEN  // 若扫描到PLC自身的IP(跳过PLC,避免重复扫描)        #iDevice := 11;    END_IF;
    // 若当前无流程且SNMP状态就绪(16#7000),启动扫描流程    IF #iAblauf = 0 AND (#STB_LSNMP_Get.status = 16#7000) THEN        #iAblauf := 1;  // 进入扫描流程第一步    END_IF;
    #"HMI-UDT".Aktuelles_Device_Scan := #iDevice;  // 向HMI同步当前扫描的设备索引END_REGION

作用:控制设备扫描的范围和顺序,避免扫描无效设备或重复扫描 PLC。

关键变量

iDevice:当前扫描的设备索引,范围由配置位cfg.%X0决定(256 或 512)。

iAblauf:扫描流程的步骤计数器(0 = 空闲,1~16 = 不同阶段),用于控制 SNMP 请求的顺序。

STB_LSNMP_Get.status:SNMP 功能块的状态码(16#7000 表示就绪),用于判断是否可发起新请求。

5.4 配置 SNMP 连接参数(typeConnParam)
#typeConnParam.hwIdentifier := 64;  // 硬件标识符(固定为64)// 设置目标设备的IP地址(根据iDevice计算)#typeConnParam.ipAddress.ADDR[1] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[0];  // 前2字节与PLC一致#typeConnParam.ipAddress.ADDR[2] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[1];IF #iDevice <= 255 THEN  // 设备索引1-255:第3字节用PLC的网段,第4字节为iDevice    #typeConnParam.ipAddress.ADDR[3] := #bPLCOktett3;    #typeConnParam.ipAddress.ADDR[4] := INT_TO_BYTE(IN := #iDevice);ELSIF #iDevice = 256 THEN  // 索引256:第4字节为0    #typeConnParam.ipAddress.ADDR[3] := #bPLCOktett3;    #typeConnParam.ipAddress.ADDR[4] := Byte#0;ELSIF #iDevice >= 257 OR #iDevice <= 511 THEN  // 索引257-511:使用第二个网段    #typeConnParam.ipAddress.ADDR[3] := INT_TO_BYTE(IN := #oktett3Subnet2);    #typeConnParam.ipAddress.ADDR[4] := INT_TO_BYTE(IN := #iDevice - 256);ELSIF #iDevice = 512 THEN  // 索引512:第4字节为0    #typeConnParam.ipAddress.ADDR[3] := INT_TO_BYTE(IN := #oktett3Subnet2);    #typeConnParam.ipAddress.ADDR[4] := Byte#0;END_IF;// SNMP连接参数#typeConnParam.connID := #VerbID_IP_V4.ID;  // 连接ID#typeConnParam.localPort := #VerbID_IP_V4.LocalPort;  // 本地端口#STB_LSNMP_Get.community := 'public';  // SNMP社区名(默认公开)#STB_LSNMP_Get.timeOut := #ZUeLesen;  // 超时时间(可配置)

作用:根据当前扫描的设备索引(iDevice)动态计算目标 IP 地址,并配置 SNMP 请求的参数(社区名、超时等)。

IP 地址规则:设备 IP 的前 2 字节与 PLC 一致,第 3 字节根据索引范围切换网段(bPLCOktett3oktett3Subnet2),第 4 字节由iDevice转换而来,确保覆盖所有设备。

5.5 设备扫描核心流程(Device Scannen)

通过CASE #iAblauf OF实现多步骤扫描,每一步对应一个 SNMP 请求(获取设备描述、运行时间、端口错误等),最终完成设备状态判断。

步骤 1-2:获取设备描述(Gerätebeschreibung)

1:  (/*oID Gerätebeschreibung senden*/)    IF #STB_LSNMP_Get.status = 16#7000 THEN  // SNMP就绪        #STB_LSNMP_Get.oID := '1.3.6.1.2.1.1.1.0';  // OID:系统描述(MIB-II的sysDescr)        #STB_LSNMP_Get.getNext := FALSE;  // 使用Get操作(非GetNext)        #STB_LSNMP_Get.execute := TRUE;  // 发起请求        #iAblauf := 2;  // 进入下一步    END_IF;2:  REGION (/*Gerätebeschreibung*/)    IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done THEN  // 请求成功        #STB_LSNMP_Get.execute := FALSE;  // 停止请求        // 将SNMP返回的字符数组转换为字符串(最多120字符)        Chars_TO_Strg(Chars := #typeVarBind.value, Strg => #STB_Chars_TO_Strg.Strg);        #Geraete[#iDevice].SysDescription := LEFT(IN := #STB_Chars_TO_Strg.Strg, L := 120);        #Geraete[#iDevice].Status := 1;  // 初始状态设为正常        #iAblauf := 3;  // 进入下一步(获取运行时间)    ELSIF #STB_LSNMP_Get.status <> 16#0 AND #STB_LSNMP_Get.error THEN  // 请求失败        #STB_LSNMP_Get.execute := FALSE;        // 更新状态(原状态+8,如正常→8,警告→16,故障→24,标记不可达)        IF #Geraete[#iDevice].Status = 1 THEN #arrStatus[#iDevice] := 8; END_IF;        #iDevice := #iDevice + 1;  // 扫描下一个设备        #iAblauf := 0;  // 重置流程    END_IF;END_REGION

OID 含义1.3.6.1.2.1.1.1.0对应 MIB-II 中的sysDescr,返回设备的制造商、型号等描述(如 “SIEMENS SCALANCE X204”)。

失败处理:若设备无响应,状态码叠加 8(标记为 “不可达”),并跳过该设备。

步骤 3-4:获取运行时间(Zeitpunkt seit letzter Neuinitialisierung)

3:  (/*oID Zeitpunkt seit letzter Neuinitialisierung senden*/)    IF #STB_LSNMP_Get.status = 16#7000 THEN        #STB_LSNMP_Get.oID := '1.3.6.1.2.1.1.3.0';  // OID:系统运行时间(sysUpTime)        #STB_LSNMP_Get.execute := TRUE;        #iAblauf := 4;    END_IF;4:  REGION (/*Zeitpunkt seit letzter Neuinitialisierung*/)    IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done THEN  // 成功        // TimeTicks(1/100秒)转换为天/时/分/秒        #SysUpTimeTemp := #typeVarBind.value;  // 原始值(4字节)        #diTemp := #SysUpTimeTemp / 8640000;  // 天(8640000 = 24*3600*100)        #Geraete[#iDevice].SysUpTime.Day := DINT_TO_INT(#diTemp);        // (省略时/分/秒计算,逻辑类似)        #iAblauf := 5;  // 进入下一步(获取端口丢弃包)    END_IF;END_REGION

OID 含义1.3.6.1.2.1.1.3.0对应sysUpTime,返回设备自启动以来的运行时间(单位:1/100 秒)。

数据转换:将 TimeTicks 转换为天 / 时 / 分 / 秒,便于 HMI 显示。

步骤 5-12:获取端口错误与丢弃数据包

通过多次 SNMP 请求获取端口的 “入站丢弃包”“入站错误包”“出站丢弃包”“出站错误包”,对应 OID 如下:

入站丢弃:1.3.6.1.2.1.2.2.1.13.x

(ifInDiscards)入站错误:1.3.6.1.2.1.2.2.1.14.x

(ifInErrors)出站丢弃:1.3.6.1.2.1.2.2.1.19.x

(ifOutDiscards)出站错误:1.3.6.1.2.1.2.2.1.20.x

(ifOutErrors)

示例(步骤 5-6:入站丢弃包)

5:  IF #STB_LSNMP_Get.status = 16#7000 THEN    #STB_LSNMP_Get.oID := #nextOID;  // 初始为'1.3.6.1.2.1.2.2.1.13.0'    #STB_LSNMP_Get.getNext := TRUE;  // 使用GetNext遍历所有端口    #STB_LSNMP_Get.execute := TRUE;    #iAblauf := 6;END_IF;6:  IF #STB_LSNMP_Get.done THEN  // 成功获取一个端口数据    #udiTemp := #typeVarBind.value;  // 存储当前端口的丢弃数    #Ports[#iInterface].ifInDiscards := #udiTemp;    #iInterface := #iInterface + 1;  // 下一个端口    #iAblauf := 5;  // 继续获取    IF #iInterface >= #AnzahlPortsMax THEN  // 达到最大端口数        #iAblauf := 7;  // 进入下一步(入站错误)    END_IF;END_IF;

逻辑:通过GetNext操作遍历设备的所有端口,将每个端口的错误 / 丢弃数存储到Ports数组,直至达到最大端口数(AnzahlPortsMax)。

步骤 13:计算总错误 / 丢弃数并判断状态

13: (/*Summe Verworfene und Fehlerhafte Pakete*/)    #iSumDiscards := 0;  // 总丢弃数    #iSumErrors := 0;    // 总错误数    FOR #i := 0 TO #Geraete[#iDevice].ifNumber - 1 DO  // 累加所有端口        #iSumDiscards := #iSumDiscards + #Ports[#i].ifInDiscards + #Ports[#i].ifOutDiscards;        #iSumErrors := #iSumErrors + #Ports[#i].ifInErrors + #Ports[#i].ifOutErrors;    END_FOR;    #Geraete[#iDevice].Ports.ifDiscards := #iSumDiscards;    #Geraete[#iDevice].Ports.ifError := #iSumErrors;
    // 与上次值比较,判断状态(同PLC的判断逻辑)    IF (错误差 > 故障阈值) THEN #Geraete[#iDevice].Status := 4;    ELSIF (错误差 > 警告阈值) THEN #Geraete[#iDevice].Status := 2;    END_IF;    #arrStatus[#iDevice] := INT_TO_BYTE(#Geraete[#iDevice].Status);  // 更新状态数组    #iAblauf := 14;

作用:累加所有端口的错误 / 丢弃数,根据与上次值的差值判断设备状态(正常 / 警告 / 故障)。

步骤 14-16:完成扫描并切换设备

14: (/*Fertig IP Adresse Teilnehmer eintragen*/)    #Geraete[#iDevice].IPAdress := #typeConnParam.ipAddress.ADDR;  // 存储设备IP    #iDevice := #iDevice + 1;  // 下一个设备    #iAblauf := 15;15:  // 处理刷新标志(若用户触发刷新,跳转到指定设备)    IF #xButton_Dialog_Refresh_Pressed THEN ... END_IF;    #iAblauf := 16;16:  // 延时1秒后重置流程,准备下一次扫描    #IEC_Timer_0_Instance(IN := (#iAblauf = 16), PT := t#1s);    IF #IEC_Timer_0_Instance.Q THEN #iAblauf := 0; END_IF;

作用:保存设备 IP,切换到下一个设备,通过延时避免扫描过于频繁。

5.6 SNMP 功能块调用与数据解析
REGION (/*LSNMP Aufruf*/)    #LSNMP_Get_Instance(        execute := #STB_LSNMP_Get.execute,        oID := #STB_LSNMP_Get.oID,        connParam := #typeConnParam,        varBinding => #typeVarBind,  // 返回的SNMP数据        status => #STB_LSNMP_Get.status  // 状态码    );END_REGIONREGION (/*Objekt ID in Values wandeln*/)    // 将OID字符串(如'1.3.6.1.2.1.2.2.1.13.5')拆分,提取端口号(最后一段)    #STB_Strg_TO_Chars.Strg := #typeVarBind.oID;    Strg_TO_Chars(Strg := #STB_Strg_TO_Chars.Strg, Chars => #STB_Strg_TO_Chars.Chars);    #STB_SPLIT.RET_VAL := SPLIT(SrcArray := #STB_Strg_TO_Chars.Chars, DstStruct => #STB_SPLIT.DstStruct);    // 提取第9段(如13)判断OID类型,用于控制扫描步骤END_REGION

作用:调用LSNMP_Get_Instance(SNMP 客户端功能块)执行请求,解析返回的 OID 和数据,提取端口号等关键信息,确保扫描步骤正确流转。

5.7 HMI 交互与数据同步
// 界面层级切换(上一页/下一页)IF #xR_Trig_Button_Detail_EbeneZurueck.Q THEN #"HMI-UDT".Detail_Ebene := #"HMI-UDT".Detail_Ebene - 1; END_IF;// 端口页切换(1-8/9-16等)IF #xR_Trig_Button_Dialog_Ports_1_8.Q THEN #iPortAnzeigeOffset := 1; END_IF;// 向HMI同步设备状态(每次显示128个设备)#return_moveblkvariant := MOVE_BLK_VARIANT(SRC := #arrStatus, DEST => #"HMI-UDT".Status, COUNT := 128);// 刷新设备详情(Profinet数据)IF #xinit THEN    #"HMI-UDT".Device_Data := #Geraete[#"HMI-UDT".Dialog_Device];  // 同步选中设备的基础数据    // 调用Profinet诊断功能块,获取端口详细数据(电缆长度、信号延迟等)    #FB_PNIO_Port_Diag_Instance(REQ := TRUE, iDevice := #iDialogDevice);    IF #FB_PNIO_Port_Diag_Instance.DONE THEN        #"HMI-UDT".PNIO_Ports[#i].CableLength := ...;  // 同步端口数据到HMI    END_IF;END_IF;

作用:根据 HMI 按钮操作切换显示的设备页 / 端口页,将设备状态、端口详情同步到

HMI-UDT(HMI 数据结构),实现界面实时更新。

5.8 状态复位功能
// 复位所有设备的错误/丢弃计数(状态重置为正常)REGION (/*Alle Devices abgleichen*/)    IF #xSF_Reset_Detail_Alle_Geraete THEN        FOR #i := 0 TO 512 DO            #Geraete[#i].Ports.ifDiscards_last := #Geraete[#i].Ports.ifDiscards;            #arrStatus[#i] := 1;  // 重置为正常        END_FOR;    END_IF;END_REGION// 复位单个设备(当前HMI选中的设备)REGION (/*Einzel Device abgleichen*/)    IF #xSF_Reset_Detail_Einzel_Geraet THEN        #i := #"HMI-UDT".Dialog_Device;        #Geraete[#i].Ports.ifDiscards_last := #Geraete[#i].Ports.ifDiscards;        #arrStatus[#i] := 1;    END_IF;END_REGION

作用:通过 HMI 的 “复位” 按钮清除历史错误 / 丢弃计数,将状态重置为正常,便于重新监控设备的后续状态变化。

总结

该功能块是一个基于 SNMP 和 Profinet 的网络设备诊断工具,核心流程包括:

    初始化 PLC 自身信息;按索引顺序扫描网络设备,通过 SNMP 获取设备描述、运行时间、端口错误 / 丢弃数据;根据数据增量判断设备状态(正常 / 警告 / 故障);同步数据到 HMI,支持界面切换、刷新、复位等操作。

代码设计体现了模块化(分区域处理)、可配置(设备数量、超时时间)、健壮性(错误处理、边沿检测)等特点,适用于汽车行业等对网络设备状态监控有严格要求的场景。

完整代码:

符号表:

代码:

REGION (/* Aenderungsjournal */)(*********************************************************************************************  Copyright  2016             SIEMENS AG D-90475 Nuernberg                            ****  All Rights Reserved         VOLKSWAGEN AG, 38436 Wolfsburg                          ****                              AUDI AG, D-85045 Ingolstadt                             ********************************************************************************************    Datum       Version     Autor       Beschreibung                                    ------------------------------------------------------------------------------------------    27.09.22    1.5.00      Lohrenz     OID Gerätebeschreibung und Laufzeit von GetNext                                        auf Get geändert.    28.04.22    1.4.00      Hennig      Werte Meldungen als Input    16.02.22    1.1.00      Lohrenz     Neuerstellung     16.02.22    1.0.01      Lohrenz     Optimierung Datenstruktur HMI-UDT                                        HMI Bedienkonzept ueberarbeitet.                                        VerbID_IP_V4 zur parametrierung von ID und                                         LocalPort hinzugefuegt.                                        Antwortzeit über ZUeLesen hinzugefuegt.                                        Mehrsprachigkeit vorbereitet      28.03.22    V1.0.02     Lohrenz     Dialog Refresh führt SNMP- und                                        Profinetdaten-Aktualsierung durch.    01.04.22    V1.0.03     Lohrenz     Korrektur bei 2 Profinetkreisen bei Refresh*****************************************************************************************)END_REGION
REGION Funktionsberschreibung    (/* In diesem FB wird die Diagnose von Teilnehmer mittels SNMP und RDREC realisiert.        Werte fuer Warnungsmeldung und Störungsmeldung noch in Erprobung */)END_REGION
REGION Konfigurationsbits (* BIT      KommentarX0       0 = 256 Teilnehmer 1 = 512 TeilnehmerX1       ReserveX2       ReserveX3       ReserveX4       ReserveX5       ReserveX6       ReserveX7       ReserveX8       ReserveX9       ReserveX10      ReserveX11      ReserveX12      ReserveX13      ReserveX14      ReserveX15      Reserve*)END_REGIONREGION (/*VISUTASTEN*/)    (/*Tasten von Visu auswerten*/)    IF "DB_ARG".PC_AKTIV = DWORD#16#00000000 THEN        #"HMI-UDT".bVisuTasten := Byte#0;    END_IF;    #xSF_Reset_Detail_Alle_Geraete := (#"HMI-UDT".bVisuTasten = Byte#1);    #xSF_Reset_Detail_Einzel_Geraet := (#"HMI-UDT".bVisuTasten = Byte#2);    #xSF_EbeneZurueck_Detail := (#"HMI-UDT".bVisuTasten = Byte#3);    #xSF_EbeneVor_Detail := (#"HMI-UDT".bVisuTasten = Byte#4);    #xSF_Button_Dialog_Ports_1_8 := (#"HMI-UDT".bVisuTasten = Byte#10);    #xSF_Button_Dialog_Ports_9_16 := (#"HMI-UDT".bVisuTasten = Byte#11);    #xSF_Button_Dialog_Ports_17_24:= (#"HMI-UDT".bVisuTasten = Byte#12);    #xSF_Button_Dialog_Ports_25_32 := (#"HMI-UDT".bVisuTasten = Byte#13);    #xSF_Button_Dialog_Refresh := (#"HMI-UDT".bVisuTasten = Byte#14);
    (/*Flankenauswertung*/)    #xR_Trig_Button_Detail_EbeneZurueck.Q := #xSF_EbeneZurueck_Detail AND NOT #xR_Trig_Button_Detail_EbeneZurueck.Edge;    #xR_Trig_Button_Detail_EbeneZurueck.Edge := #xSF_EbeneZurueck_Detail;    #xR_Trig_Button_Detail_EbeneVor.Q := #xSF_EbeneVor_Detail AND NOT #xR_Trig_Button_Detail_EbeneVor.Edge;    #xR_Trig_Button_Detail_EbeneVor.Edge := #xSF_EbeneVor_Detail;    #xR_Trig_Button_Dialog_Ports_1_8.Q := #xSF_Button_Dialog_Ports_1_8 AND NOT #xR_Trig_Button_Dialog_Ports_1_8.Edge;    #xR_Trig_Button_Dialog_Ports_1_8.Edge := #xSF_Button_Dialog_Ports_1_8;    #xR_Trig_Button_Dialog_Ports_9_16.Q := #xSF_Button_Dialog_Ports_9_16 AND NOT #xR_Trig_Button_Dialog_Ports_9_16.Edge;    #xR_Trig_Button_Dialog_Ports_9_16.Edge := #xSF_Button_Dialog_Ports_9_16;    #xR_Trig_Button_Dialog_Ports_17_24.Q := #xSF_Button_Dialog_Ports_17_24 AND NOT #xR_Trig_Button_Dialog_Ports_17_24.Edge;    #xR_Trig_Button_Dialog_Ports_17_24.Edge := #xSF_Button_Dialog_Ports_17_24;    #xR_Trig_Button_Dialog_Ports_25_32.Q := #xSF_Button_Dialog_Ports_25_32 AND NOT #xR_Trig_Button_Dialog_Ports_25_32.Edge;    #xR_Trig_Button_Dialog_Ports_25_32.Edge := #xSF_Button_Dialog_Ports_25_32;    #xR_Trig_Button_Dialog_Refresh.Q := #xSF_Button_Dialog_Refresh AND NOT #xR_Trig_Button_Dialog_Refresh.Edge;    #xR_Trig_Button_Dialog_Refresh.Edge := #xSF_Button_Dialog_Refresh;
    IF #xR_Trig_Button_Dialog_Refresh.Q AND NOT #xButton_Dialog_Refresh_Pressed THEN        #xButton_Dialog_Refresh_Pressed := TRUE;    END_IF;END_REGION
REGION (/*Ablauf*/)    IF "DB_ARG".NEUSTART_STEU_EIN THEN        #xinit := false;    END_IF;
    REGION (/*SPS IP-Adresse lesen*/)        (/*CPU Geräteadresse ist 10 da im VASS bei Oktett4 10 vorgeschrieben ist.*/)        (/*die Tatsächliche Geräteadresse einer CPU ist aber 0 da vom System vorgegeben.*/)        (/*dieses Verhalten ist so gewollt.*/)        IF NOT #xinit THEN            #FB_PNIO_Port_Diag_Instance(REQ := TRUE,                                        iMaster := 100,                                        iDevice := 0);            IF #FB_PNIO_Port_Diag_Instance.DONE THEN                #Geraete[10].IPAdress[0] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[0];                #Geraete[10].IPAdress[1] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[1];                #Geraete[10].IPAdress[2] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[2];                #bPLCOktett3 := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[2];                #Geraete[10].IPAdress[3] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[3];                #DeviceIP_PLC := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[3];                #Geraete[10].SysDescription := 'PLC';                #Geraete[10].Ports.ifDiscards := #FB_PNIO_Port_Diag_Instance.PortData.Ports[0].InDiscard +                #FB_PNIO_Port_Diag_Instance.PortData.Ports[0].OutDiscard +                #FB_PNIO_Port_Diag_Instance.PortData.Ports[1].InDiscard +                #FB_PNIO_Port_Diag_Instance.PortData.Ports[1].OutDiscard;                #Geraete[10].Ports.ifError := #FB_PNIO_Port_Diag_Instance.PortData.Ports[0].InError +                #FB_PNIO_Port_Diag_Instance.PortData.Ports[0].OutError +                #FB_PNIO_Port_Diag_Instance.PortData.Ports[1].InError +                #FB_PNIO_Port_Diag_Instance.PortData.Ports[1].OutError;                #Geraete[10].ifNumber := #FB_PNIO_Port_Diag_Instance.Ports;                IF (#Geraete[10].Ports.ifError - #Geraete[10].Ports.ifError_last) > #diff_err OR                    (#Geraete[10].Ports.ifDiscards - #Geraete[10].Ports.ifDiscards_last) > #diff_err THEN                    #Geraete[10].Status := 4;                ELSIF (#Geraete[10].Ports.ifError - #Geraete[10].Ports.ifError_last) > #diff_warn OR                    (#Geraete[10].Ports.ifDiscards - #Geraete[10].Ports.ifDiscards_last) > #diff_warn THEN                    #Geraete[10].Status := 2;                ELSE                    #Geraete[10].Status := 1;                END_IF;                #arrStatus[10] := INT_TO_BYTE(IN := #Geraete[10].Status);                #iPortAnzeigeOffset := 1;                #"HMI-UDT".bRueckmeldungTasten := 10;                #xinit := true;            END_IF;        END_IF;    END_REGION
        REGION (/*Scanner*/)            IF #iDevice < 1 THEN                #iDevice := 1;            ELSIF (#iDevice > 512 AND #cfg.%X0) OR (#iDevice > 256 AND NOT #cfg.%X0) THEN                #iDevice := 1;            ELSIF #iDevice = #DeviceIP_PLC THEN (/*PLC im VASS Standard darf nicht gescannt werden*/)                #iDevice := 11;            END_IF;            IF #iAblauf = 0 AND (#STB_LSNMP_Get.status = 16#7000) THEN                #iAblauf := 1;            END_IF;            #"HMI-UDT".Aktuelles_Device_Scan := #iDevice;        END_REGION
        #typeConnParam.hwIdentifier := 64;
        #typeConnParam.ipAddress.ADDR[1] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[0];        #typeConnParam.ipAddress.ADDR[2] := #FB_PNIO_Port_Diag_Instance.PortData.ipAdress[1];        IF #iDevice <= 255 THEN            #typeConnParam.ipAddress.ADDR[3] := #bPLCOktett3;            #typeConnParam.ipAddress.ADDR[4] := INT_TO_BYTE(IN := #iDevice);        ELSIF #iDevice = 256 THEN            #typeConnParam.ipAddress.ADDR[3] := #bPLCOktett3;            #typeConnParam.ipAddress.ADDR[4] := INT_TO_BYTE(IN := 0);        ELSIF #iDevice >= 257 OR #iDevice <= 511 THEN            #typeConnParam.ipAddress.ADDR[3] := INT_TO_BYTE(IN := #oktett3Subnet2);            #typeConnParam.ipAddress.ADDR[4] := INT_TO_BYTE(IN := #iDevice - 256);        ELSIF #iDevice = 512 THEN            #typeConnParam.ipAddress.ADDR[3] := INT_TO_BYTE(IN := #oktett3Subnet2);            #typeConnParam.ipAddress.ADDR[4] := INT_TO_BYTE(IN := 0);        END_IF;
        #typeConnParam.connID := #VerbID_IP_V4.ID;        #typeConnParam.localPort := #VerbID_IP_V4.LocalPort;        #STB_LSNMP_Get.community := 'public';        #STB_LSNMP_Get.timeOut := #ZUeLesen;
        REGION (/*Device Scannen*/)            CASE #iAblauf OF                0:  (/*kein Ablauf*/)                    ;                1:  (/*oID Gerätebeschreibung senden*/)                    IF #STB_LSNMP_Get.status = 16#7000 THEN                        #STB_LSNMP_Get.oID := '1.3.6.1.2.1.1.1.0';                        #STB_LSNMP_Get.getNext := FALSE;                        #STB_LSNMP_Get.execute := TRUE;                        #iAblauf := 2;                    END_IF;                2:                    REGION (/*Gerätebeschreibung*/)                        IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done AND (#typeVarBind.oID = '1.3.6.1.2.1.1.1.0') THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            #STB_Chars_TO_Strg.pChars := 0;                            #STB_Chars_TO_Strg.Cnt := 0;                            Chars_TO_Strg(Chars := #typeVarBind.value,                                          pChars := #STB_Chars_TO_Strg.pChars,                                          Cnt := #STB_Chars_TO_Strg.Cnt,                                          Strg => #STB_Chars_TO_Strg.Strg);                            #Geraete[#iDevice].SysDescription := LEFT(IN := #STB_Chars_TO_Strg.Strg, L := 120);                            #Geraete[#iDevice].Status := 1;                            #iAblauf := 3;                        ELSIF #STB_LSNMP_Get.status <> 16#0 AND #STB_LSNMP_Get.error THEN                            #STB_LSNMP_Get.oID := '';                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            #iAblauf := 0;                            IF #Geraete[#iDevice].Status = 1 THEN                                #arrStatus[#iDevice] := 8;                            ELSIF #Geraete[#iDevice].Status = 2 THEN                                #arrStatus[#iDevice] := 16;                            ELSIF #Geraete[#iDevice].Status = 4 THEN                                #arrStatus[#iDevice] := 24;                            END_IF;                            #iDevice := #iDevice + 1;                            IF #xButton_Dialog_Refresh_Pressed AND NOT #xSelected_Device_Scan THEN                                #iDevice := #iDialogDevice;                                #xSelected_Device_Scanned := FALSE;                                #xSelected_Device_Scan := TRUE;                            ELSIF #xButton_Dialog_Refresh_Pressed AND #xSelected_Device_Scan THEN                                #xSelected_Device_Scanned := TRUE;                                #xSelected_Device_Scan := FALSE;                            END_IF;                        END_IF;                    END_REGION                3: (/*oID Zeitpunkt seit der letzten Neuinialisierung senden*/)                    IF #STB_LSNMP_Get.status = 16#7000 THEN                        #STB_LSNMP_Get.oID := '1.3.6.1.2.1.1.3.0';                        #STB_LSNMP_Get.getNext := FALSE;                        #STB_LSNMP_Get.execute := TRUE;                        #iAblauf := 4;                    END_IF;                4:                    REGION (/*Zeitpunkt seit der letzten Neuinitialisierung*/)                        IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            (/*SysUpTime TimeTicks zu Zeit Tage, Stunden, Minuten, Sekunden berechnen*/)                            IF #typeVarBind.type = 16#43 THEN  (/*Datatype is TimeTicks*/)                                IF #typeVarBind.length = 1 THEN                                    #SysUpTimeTemp.%B0 := #typeVarBind.value[0];                                    #SysUpTimeTemp.%B1 := 0;                                    #SysUpTimeTemp.%B2 := 0;                                    #SysUpTimeTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 2 THEN                                    #SysUpTimeTemp.%B0 := #typeVarBind.value[1];                                    #SysUpTimeTemp.%B1 := #typeVarBind.value[0];                                    #SysUpTimeTemp.%B2 := 0;                                    #SysUpTimeTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 3 THEN                                    #SysUpTimeTemp.%B0 := #typeVarBind.value[2];                                    #SysUpTimeTemp.%B1 := #typeVarBind.value[1];                                    #SysUpTimeTemp.%B2 := #typeVarBind.value[0];                                    #SysUpTimeTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 4 THEN                                    #SysUpTimeTemp.%B0 := #typeVarBind.value[3];                                    #SysUpTimeTemp.%B1 := #typeVarBind.value[2];                                    #SysUpTimeTemp.%B2 := #typeVarBind.value[1];                                    #SysUpTimeTemp.%B3 := #typeVarBind.value[0];                                END_IF;                                // days                                #diTemp := #SysUpTimeTemp / 8640000;                                #iTemp := DINT_TO_INT(IN := #diTemp);                                #SysUpTimeTemp := #SysUpTimeTemp - (INT_TO_DINT(#iTemp) * L#8640000);                                #Geraete[#iDevice].SysUpTime.Day := #iTemp;                                // hours                                #diTemp := #SysUpTimeTemp / 360000;                                #iTemp := DINT_TO_INT(IN := #diTemp);                                #SysUpTimeTemp := #SysUpTimeTemp - (INT_TO_DINT(#iTemp) * L#360000);                                #Geraete[#iDevice].SysUpTime.Hours := #iTemp;                                // minutes                                #diTemp := #SysUpTimeTemp / 6000;                                #iTemp := DINT_TO_INT(IN := #diTemp);                                #SysUpTimeTemp := #SysUpTimeTemp - (INT_TO_DINT(#iTemp) * L#6000);                                #Geraete[#iDevice].SysUpTime.Minutes := #iTemp;                                // seconds                                #diTemp := #SysUpTimeTemp / 100;                                #iTemp := DINT_TO_INT(IN := #diTemp);                                #SysUpTimeTemp := #SysUpTimeTemp - (#iTemp * 100);                                #Geraete[#iDevice].SysUpTime.Seconds := #iTemp;                            END_IF;                            #nextOID := '1.3.6.1.2.1.2.2.1.13.0'; (/*Erster IndexOID*/)                            #iInterface := 0;                            #iAblauf := 5;                        ELSIF #STB_LSNMP_Get.status <> 16#0 AND #STB_LSNMP_Get.error THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #Geraete[#iDevice].Status = 1 THEN                                #arrStatus[#iDevice] := 8;                            ELSIF #Geraete[#iDevice].Status = 2 THEN                                #arrStatus[#iDevice] := 16;                            ELSIF #Geraete[#iDevice].Status = 4 THEN                                #arrStatus[#iDevice] := 24;                            END_IF;                            #iDevice := #iDevice + 1;                            #iAblauf := 15;                        END_IF;                    END_REGION                5: (/*oID Anzahl verworfener eigehende Pakete senden*/)                    IF #STB_LSNMP_Get.status = 16#7000 THEN                        #STB_LSNMP_Get.oID := #nextOID;                        #STB_LSNMP_Get.getNext := TRUE;                        #STB_LSNMP_Get.execute := TRUE;                        #iAblauf := 6;                    END_IF;                6:                    REGION (/*Anzahl verworfener eingehende Pakete*/)                        IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #STB_STRG_VAL.outputVAL[9] = 13 AND NOT (#iInterface >= #AnzahlPortsMax) THEN (/*Wiederholen bis OID Ende erreicht wurde*/)                                IF #typeVarBind.length = 1 THEN                                    #udiTemp.%B0 := #typeVarBind.value[0];                                    #udiTemp.%B1 := 0;                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 2 THEN                                    #udiTemp.%B0 := #typeVarBind.value[1];                                    #udiTemp.%B1 := #typeVarBind.value[0];                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 3 THEN                                    #udiTemp.%B0 := #typeVarBind.value[2];                                    #udiTemp.%B1 := #typeVarBind.value[1];                                    #udiTemp.%B2 := #typeVarBind.value[0];                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 4 THEN                                    #udiTemp.%B0 := #typeVarBind.value[3];                                    #udiTemp.%B1 := #typeVarBind.value[2];                                    #udiTemp.%B2 := #typeVarBind.value[1];                                    #udiTemp.%B3 := #typeVarBind.value[0];                                END_IF;                                #Ports[#iInterface].ifInDiscards := #udiTemp;                                #nextOID := #typeVarBind.oID;                                #iInterface := #iInterface + 1;                                #iAblauf := 5;                            ELSE                                #nextOID := '1.3.6.1.2.1.2.2.1.14.0'; (/*Erster IndexOID*/)                                #Geraete[#iDevice].ifNumber := #iInterface;                                #iInterface := 0;                                #iAblauf := 7;                            END_IF;                        ELSIF #STB_LSNMP_Get.status <> 16#0 AND #STB_LSNMP_Get.error THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #Geraete[#iDevice].Status = 1 THEN                                #arrStatus[#iDevice] := 8;                            ELSIF #Geraete[#iDevice].Status = 2 THEN                                #arrStatus[#iDevice] := 16;                            ELSIF #Geraete[#iDevice].Status = 4 THEN                                #arrStatus[#iDevice] := 24;                            END_IF;                            #iAblauf := 15;                        END_IF;                    END_REGION                7: (/*oID Anzahl Fehler eigehende Pakete senden*/)                    IF #STB_LSNMP_Get.status = 16#7000 THEN                        #STB_LSNMP_Get.oID := #nextOID;                        #STB_LSNMP_Get.getNext := TRUE;                        #STB_LSNMP_Get.execute := TRUE;                        #iAblauf := 8;                    END_IF;                8:                    REGION (/*Anzahl Fehler eingehende Pakete*/)                        IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #STB_STRG_VAL.outputVAL[9] = 14 AND NOT (#iInterface >= #AnzahlPortsMax) THEN (/*Wiederholen bis OID Ende erreicht wurde*/)                                IF #typeVarBind.length = 1 THEN                                    #udiTemp.%B0 := #typeVarBind.value[0];                                    #udiTemp.%B1 := 0;                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 2 THEN                                    #udiTemp.%B0 := #typeVarBind.value[1];                                    #udiTemp.%B1 := #typeVarBind.value[0];                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 3 THEN                                    #udiTemp.%B0 := #typeVarBind.value[2];                                    #udiTemp.%B1 := #typeVarBind.value[1];                                    #udiTemp.%B2 := #typeVarBind.value[0];                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 4 THEN                                    #udiTemp.%B0 := #typeVarBind.value[3];                                    #udiTemp.%B1 := #typeVarBind.value[2];                                    #udiTemp.%B2 := #typeVarBind.value[1];                                    #udiTemp.%B3 := #typeVarBind.value[0];                                END_IF;                                #Ports[#iInterface].ifInErrors := #udiTemp;                                #nextOID := #typeVarBind.oID;                                #iInterface := #iInterface + 1;                                #iAblauf := 7;                            ELSE                                #nextOID := '1.3.6.1.2.1.2.2.1.19.0'; (/*Erster IndexOID*/)                                #iInterface := 0;                                #iAblauf := 9;                            END_IF;                        ELSIF #STB_LSNMP_Get.status <> 16#0 AND #STB_LSNMP_Get.error THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #Geraete[#iDevice].Status = 1 THEN                                #arrStatus[#iDevice] := 8;                            ELSIF #Geraete[#iDevice].Status = 2 THEN                                #arrStatus[#iDevice] := 16;                            ELSIF #Geraete[#iDevice].Status = 4 THEN                                #arrStatus[#iDevice] := 24;                            END_IF;                            #iAblauf := 15;                        END_IF;                    END_REGION                9: (/*oID verworfener ausgehnde Pakete senden*/)                    IF #STB_LSNMP_Get.status = 16#7000 THEN                        #STB_LSNMP_Get.oID := #nextOID;                        #STB_LSNMP_Get.getNext := TRUE;                        #STB_LSNMP_Get.execute := TRUE;                        #iAblauf := 10;                    END_IF;                10:                    REGION (/*Anzahl verworfener ausgehende Pakete senden*/)                        IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #STB_STRG_VAL.outputVAL[9] = 19 AND NOT (#iInterface >= #AnzahlPortsMax) THEN (/*Wiederholen bis OID Ende erreicht wurde*/)                                IF #typeVarBind.length = 1 THEN                                    #udiTemp.%B0 := #typeVarBind.value[0];                                    #udiTemp.%B1 := 0;                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 2 THEN                                    #udiTemp.%B0 := #typeVarBind.value[1];                                    #udiTemp.%B1 := #typeVarBind.value[0];                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 3 THEN                                    #udiTemp.%B0 := #typeVarBind.value[2];                                    #udiTemp.%B1 := #typeVarBind.value[1];                                    #udiTemp.%B2 := #typeVarBind.value[0];                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 4 THEN                                    #udiTemp.%B0 := #typeVarBind.value[3];                                    #udiTemp.%B1 := #typeVarBind.value[2];                                    #udiTemp.%B2 := #typeVarBind.value[1];                                    #udiTemp.%B3 := #typeVarBind.value[0];                                END_IF;                                #Ports[#iInterface].ifOutDiscards := #udiTemp;                                #nextOID := #typeVarBind.oID;                                #iInterface := #iInterface + 1;                                #iAblauf := 9;                            ELSE                                #nextOID := '1.3.6.1.2.1.2.2.1.20.0'; (/*Erster IndexOID*/)                                #iInterface := 0;                                #iAblauf := 11;                            END_IF;                        ELSIF #STB_LSNMP_Get.status <> 16#0 AND #STB_LSNMP_Get.error THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #Geraete[#iDevice].Status = 1 THEN                                #arrStatus[#iDevice] := 8;                            ELSIF #Geraete[#iDevice].Status = 2 THEN                                #arrStatus[#iDevice] := 16;                            ELSIF #Geraete[#iDevice].Status = 4 THEN                                #arrStatus[#iDevice] := 24;                            END_IF;                            #iAblauf := 15;                        END_IF;                    END_REGION                11: (/*oID Anzahl Fehler ausgehende Pakete senden*/)                    IF #STB_LSNMP_Get.status = 16#7000 THEN                        #STB_LSNMP_Get.oID := #nextOID;                        #STB_LSNMP_Get.getNext := TRUE;                        #STB_LSNMP_Get.execute := TRUE;                        #iAblauf := 12;                    END_IF;                12:                    REGION (/*Anzahl Fehler ausgehende Pakete*/)                        IF #STB_LSNMP_Get.status = 16#0 AND #STB_LSNMP_Get.done THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #STB_STRG_VAL.outputVAL[9] = 20 AND NOT (#iInterface >= #AnzahlPortsMax) THEN (/*Wiederholen bis OID Ende erreicht wurde*/)                                IF #typeVarBind.length = 1 THEN                                    #udiTemp.%B0 := #typeVarBind.value[0];                                    #udiTemp.%B1 := 0;                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 2 THEN                                    #udiTemp.%B0 := #typeVarBind.value[1];                                    #udiTemp.%B1 := #typeVarBind.value[0];                                    #udiTemp.%B2 := 0;                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 3 THEN                                    #udiTemp.%B0 := #typeVarBind.value[2];                                    #udiTemp.%B1 := #typeVarBind.value[1];                                    #udiTemp.%B2 := #typeVarBind.value[0];                                    #udiTemp.%B3 := 0;                                ELSIF #typeVarBind.length = 4 THEN                                    #udiTemp.%B0 := #typeVarBind.value[3];                                    #udiTemp.%B1 := #typeVarBind.value[2];                                    #udiTemp.%B2 := #typeVarBind.value[1];                                    #udiTemp.%B3 := #typeVarBind.value[0];                                END_IF;                                #Ports[#iInterface].ifOutErrors := #udiTemp;                                #nextOID := #typeVarBind.oID;                                #iInterface := #iInterface + 1;                                #iAblauf := 11;                            ELSE                                #nextOID := '';                                #iInterface := 0;                                #iAblauf := 13;                            END_IF;                        ELSIF #STB_LSNMP_Get.status <> 16#0 AND #STB_LSNMP_Get.error THEN                            #STB_LSNMP_Get.getNext := FALSE;                            #STB_LSNMP_Get.execute := FALSE;                            IF #Geraete[#iDevice].Status = 1 THEN                                #arrStatus[#iDevice] := 8;                            ELSIF #Geraete[#iDevice].Status = 2 THEN                                #arrStatus[#iDevice] := 16;                            ELSIF #Geraete[#iDevice].Status = 4 THEN                                #arrStatus[#iDevice] := 24;                            END_IF;                            #iAblauf := 15;                        END_IF;                    END_REGION                13: (/*Summe Verworfene und Fehlerhafte Pakete*/)                    #iSumDiscards := 0;                    #iSumErrors := 0;                    FOR #i := 0 TO #Geraete[#iDevice].ifNumber - 1 DO                        #iSumDiscards := #iSumDiscards + #Ports[#i].ifInDiscards;                        #iSumDiscards := #iSumDiscards + #Ports[#i].ifOutDiscards;                        #iSumErrors := #iSumErrors + #Ports[#i].ifInErrors;                        #iSumErrors := #iSumErrors + #Ports[#i].ifOutErrors;                    END_FOR;                    #Geraete[#iDevice].Ports.ifDiscards := #iSumDiscards;                    #Geraete[#iDevice].Ports.ifError := #iSumErrors;
                    IF (#Geraete[#iDevice].Ports.ifError - #Geraete[#iDevice].Ports.ifError_last) > #diff_err OR                        (#Geraete[#iDevice].Ports.ifDiscards - #Geraete[#iDevice].Ports.ifDiscards_last) > #diff_err THEN                        #Geraete[#iDevice].Status := 4;                    ELSIF (#Geraete[#iDevice].Ports.ifError - #Geraete[#iDevice].Ports.ifError_last) > #diff_warn OR                        (#Geraete[#iDevice].Ports.ifDiscards - #Geraete[#iDevice].Ports.ifDiscards_last) > #diff_warn THEN                        #Geraete[#iDevice].Status := 2;                    END_IF;                    #arrStatus[#iDevice] := INT_TO_BYTE(IN := #Geraete[#iDevice].Status);                    #iAblauf := 14;                14: (/*Fertig IP Adresse Teilnehmer eintragen*/)                    IF #STB_LSNMP_Get.status = 16#7000 THEN                        #Geraete[#iDevice].IPAdress[0] := #typeConnParam.ipAddress.ADDR[1];                        #Geraete[#iDevice].IPAdress[1] := #typeConnParam.ipAddress.ADDR[2];                        #Geraete[#iDevice].IPAdress[2] := #typeConnParam.ipAddress.ADDR[3];                        #Geraete[#iDevice].IPAdress[3] := #typeConnParam.ipAddress.ADDR[4];                        #iDevice := #iDevice + 1;                        #iAblauf := 15;                    END_IF;                15:                    IF #xButton_Dialog_Refresh_Pressed AND NOT #xSelected_Device_Scan THEN                        #iDevice := #iDialogDevice;                        #xSelected_Device_Scanned := FALSE;                        #xSelected_Device_Scan := TRUE;                    ELSIF #xButton_Dialog_Refresh_Pressed AND #xSelected_Device_Scan THEN                        #xSelected_Device_Scanned := TRUE;                        #xSelected_Device_Scan := FALSE;                    END_IF;                    #iAblauf := 16;                16:                    (/*Schritt 16 darf nicht geloescht werden*/)                    ;            END_CASE;
            #IEC_Timer_0_Instance(IN := (#iAblauf = 16),                                  PT := t#1s);            IF #IEC_Timer_0_Instance.Q THEN                #iAblauf := 0;            END_IF;
            REGION (/*LSNMP Aufruf*/)                #LSNMP_Get_Instance(execute := #STB_LSNMP_Get.execute,                                    abort := #STB_LSNMP_Get.abort,                                    getNext := #STB_LSNMP_Get.getNext,                                    community := #STB_LSNMP_Get.community,                                    oID := #STB_LSNMP_Get.oID,                                    timeOut := #STB_LSNMP_Get.timeOut,                                    done => #STB_LSNMP_Get.done,                                    busy => #STB_LSNMP_Get.busy,                                    aborted => #STB_LSNMP_Get.aborted,                                    error => #STB_LSNMP_Get.error,                                    status => #STB_LSNMP_Get.status,                                    diagnostics => #typeDiagnostics,                                    connParam := #typeConnParam,                                    varBinding := #typeVarBind);            END_REGION
            REGION (/*Objekt ID in Values wandeln*/)                FOR #i := 0 TO 255 DO                    #STB_Strg_TO_Chars.Chars[#i] := ' ';                END_FOR;
                #STB_Strg_TO_Chars.Strg := #typeVarBind.oID;
                Strg_TO_Chars(Strg := #STB_Strg_TO_Chars.Strg,                              pChars := #STB_Strg_TO_Chars.pChars,                              Cnt => #STB_Strg_TO_Chars.Cnt,                              Chars := #STB_Strg_TO_Chars.Chars);                #STB_SPLIT.Position := 0;
                FOR #i := 0 TO 10 DO                    #STB_SPLIT.DstStruct[#i] := '';                END_FOR;
                #STB_SPLIT.RET_VAL := SPLIT(Mode := #STB_SPLIT.Mode,                                            RecSeparator := #STB_SPLIT.RecSeparator,                                            EndSeparator := #STB_SPLIT.EndSeparator,                                            SrcArray := #STB_Strg_TO_Chars.Chars,                                            Count => #STB_SPLIT.Count,                                            DstStruct := #STB_SPLIT.DstStruct,                                            Position := #STB_SPLIT.Position);
                FOR #i := 0 TO 10 DO                    STRG_VAL(IN := #STB_SPLIT.DstStruct[#i],                             FORMAT := #STB_STRG_VAL.resultSformat,                             P := #STB_STRG_VAL.pointerSTRG,                             OUT => #STB_STRG_VAL.outputVAL[#i]);                END_FOR;            END_REGION        END_REGION
        IF #xR_Trig_Button_Detail_EbeneZurueck.Q THEN            #"HMI-UDT".Detail_Ebene := #"HMI-UDT".Detail_Ebene - 1;            IF #"HMI-UDT".Detail_Ebene < 0 THEN                #"HMI-UDT".Detail_Ebene := 0;            END_IF;        END_IF;
        IF #xR_Trig_Button_Detail_EbeneVor.Q THEN            #"HMI-UDT".Detail_Ebene := #"HMI-UDT".Detail_Ebene + 1;            IF #"HMI-UDT".Detail_Ebene > 3 AND #cfg.%X0 THEN                #"HMI-UDT".Detail_Ebene := 3;            ELSIF #"HMI-UDT".Detail_Ebene > 1 AND NOT #cfg.%X0 THEN                #"HMI-UDT".Detail_Ebene := 1;            END_IF;        END_IF;
        CASE #"HMI-UDT".Detail_Ebene OF            0:                #srcIndex := 0;            1:                #srcIndex := 128;            2:                #srcIndex := 256;            3:                #srcIndex := 384;        END_CASE;
        #destIndex := 0;        #return_moveblkvariant :=  MOVE_BLK_VARIANT(SRC := #arrStatus, COUNT := 128, SRC_INDEX := #srcIndex, DEST_INDEX := #destIndex, DEST => #"HMI-UDT".Status);
        REGION (/*Dialog Diagnose Daten*/)            #"HMI-UDT".Device_Data.Ports.ifDiscards := #"HMI-UDT".Device_Data.Ports.ifDiscards - #"HMI-UDT".Device_Data.Ports.ifDiscards_last;            #"HMI-UDT".Device_Data.Ports.ifError := #"HMI-UDT".Device_Data.Ports.ifError - #"HMI-UDT".Device_Data.Ports.ifError_last;            IF #xR_Trig_Button_Dialog_Ports_1_8.Q THEN                #iPortAnzeigeOffset := 1;                #"HMI-UDT".bRueckmeldungTasten := 10;            ELSIF #xR_Trig_Button_Dialog_Ports_9_16.Q THEN                #iPortAnzeigeOffset := 9;                #"HMI-UDT".bRueckmeldungTasten := 11;            ELSIF #xR_Trig_Button_Dialog_Ports_17_24.Q THEN                #iPortAnzeigeOffset := 17;                #"HMI-UDT".bRueckmeldungTasten := 12;            ELSIF #xR_Trig_Button_Dialog_Ports_25_32.Q THEN                #iPortAnzeigeOffset := 25;                #"HMI-UDT".bRueckmeldungTasten := 13;            END_IF;
            IF #xinit THEN                #"HMI-UDT".Device_Data := #Geraete[#"HMI-UDT".Dialog_Device];
                IF #xHM_Dialog_Device <> #"HMI-UDT".Dialog_Device OR #xSelected_Device_Scanned THEN                    IF #"HMI-UDT".Dialog_Device = #DeviceIP_PLC THEN                        #iDialogDevice := 0;                    ELSE                        #iDialogDevice := #"HMI-UDT".Dialog_Device;                    END_IF;
                    #FB_PNIO_Port_Diag_Instance(REQ := TRUE,                                                iMaster := 100,                                                iDevice := #iDialogDevice);                    IF #FB_PNIO_Port_Diag_Instance.DONE THEN                        #ModulName := '';                        #iTemp := LEN(IN := #FB_PNIO_Port_Diag_Instance.PortData.ProfinetDeviceName);                        FOR #i := 0 TO #iTemp DO // LOWER TO UPPER                            #bTemp := CHAR_TO_BYTE(IN := #FB_PNIO_Port_Diag_Instance.PortData.ProfinetDeviceName[#i]);                            IF BYTE_TO_INT(IN := #bTemp) > 96 AND BYTE_TO_INT(IN := #bTemp) < 123 THEN                                #bTemp := #bTemp AND 16#DF;                            ELSE                                #bTemp := #bTemp;                            END_IF;                            #ModulName[#i] := BYTE_TO_CHAR(IN := #bTemp);                            #arrScanPortsData := #FB_PNIO_Port_Diag_Instance.PortData.Ports;
                        END_FOR;                        #"HMI-UDT".Device_PNName := #ModulName;                        #xHM_Dialog_Device := #"HMI-UDT".Dialog_Device;                        #isPNIODevice := TRUE;                        IF #xSelected_Device_Scanned THEN                            #xSelected_Device_Scanned := FALSE;                            #xSelected_Device_Scan := FALSE;                            #xButton_Dialog_Refresh_Pressed := FALSE;                        END_IF;                    ELSIF #FB_PNIO_Port_Diag_Instance.ERROR THEN                        #ModulName := '';                        #"HMI-UDT".Device_PNName := 'SNMP Device';                        #isPNIODevice := FALSE;                        IF #xSelected_Device_Scanned THEN                            #xSelected_Device_Scanned := FALSE;                            #xSelected_Device_Scan := FALSE;                            #xButton_Dialog_Refresh_Pressed := FALSE;                        END_IF;                    END_IF;
                END_IF;
                IF #isPNIODevice THEN                    FOR #i := 0 TO 7 DO                        #"HMI-UDT".PNIO_Ports[#i].PortNr := #i + #iPortAnzeigeOffset;                        #"HMI-UDT".PNIO_Ports[#i].CableLength := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].CableLength;                        #"HMI-UDT".PNIO_Ports[#i].InDiscard := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].InDiscard;                        #"HMI-UDT".PNIO_Ports[#i].InError := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].InError;                        #"HMI-UDT".PNIO_Ports[#i].InOctets := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].InOctets;                        #"HMI-UDT".PNIO_Ports[#i].MAUType := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].MAUType;                        #"HMI-UDT".PNIO_Ports[#i].MediaType := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].MediaType;                        #"HMI-UDT".PNIO_Ports[#i].OutDiscard := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].OutDiscard;                        #"HMI-UDT".PNIO_Ports[#i].OutError := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].OutError;                        #"HMI-UDT".PNIO_Ports[#i].OutOctets := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].OutOctets;                        #"HMI-UDT".PNIO_Ports[#i].POFPowerBudget := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].POFPowerBudget;                        #"HMI-UDT".PNIO_Ports[#i].PortStatusLink := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].PortStatusLink;                        #"HMI-UDT".PNIO_Ports[#i].PortStatusPort := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].PortStatusPort;                        #"HMI-UDT".PNIO_Ports[#i].Signallaufzeit := #FB_PNIO_Port_Diag_Instance.PortData.Ports[#i + #iPortAnzeigeOffset - 1].Signallaufzeit;                        #Geraete[#iDialogDevice].ifNumber := #FB_PNIO_Port_Diag_Instance.Ports;                    END_FOR;                ELSE                    FOR #i := 0 TO 7 DO                        #"HMI-UDT".PNIO_Ports[#i].PortNr := #i + #iPortAnzeigeOffset;                        #"HMI-UDT".PNIO_Ports[#i].CableLength := 0;                        #"HMI-UDT".PNIO_Ports[#i].InDiscard := 0;                        #"HMI-UDT".PNIO_Ports[#i].InError := 0;                        #"HMI-UDT".PNIO_Ports[#i].InOctets := 0;                        #"HMI-UDT".PNIO_Ports[#i].MAUType := 0;                        #"HMI-UDT".PNIO_Ports[#i].MediaType := 0;                        #"HMI-UDT".PNIO_Ports[#i].OutDiscard := 0;                        #"HMI-UDT".PNIO_Ports[#i].OutError := 0;                        #"HMI-UDT".PNIO_Ports[#i].OutOctets := 0;                        #"HMI-UDT".PNIO_Ports[#i].POFPowerBudget := 0;                        #"HMI-UDT".PNIO_Ports[#i].PortStatusLink := 0;                        #"HMI-UDT".PNIO_Ports[#i].PortStatusPort := 0;                        #"HMI-UDT".PNIO_Ports[#i].Signallaufzeit := 0;                    END_FOR;                END_IF;            END_IF;        END_REGION
        REGION (/*Alle Devices abgleichen*/)            IF #xSF_Reset_Detail_Alle_Geraete THEN                FOR #i := 0 TO 512 DO                    #Geraete[#i].Ports.ifDiscards_last := #Geraete[#i].Ports.ifDiscards;                    #Geraete[#i].Ports.ifError_last := #Geraete[#i].Ports.ifError;                    IF #arrStatus[#i] > 1 THEN                        #arrStatus[#i] := 1;                    END_IF;                END_FOR;            END_IF;        END_REGION
        REGION (/*Einzel Device abgleichen*/)            IF #xSF_Reset_Detail_Einzel_Geraet THEN                #i := #"HMI-UDT".Dialog_Device;                #Geraete[#i].Ports.ifDiscards_last := #Geraete[#i].Ports.ifDiscards;                #Geraete[#i].Ports.ifError_last := #Geraete[#i].Ports.ifError;                IF #arrStatus[#i] > 1 THEN                    #arrStatus[#i] := 1;                END_IF;            END_IF;        END_REGION
        REGION (/* Visuwerte */)            #"HMI-UDT".dwVisuwert1.%X0 := #xButton_Dialog_Refresh_Pressed;        END_REGION
END_REGION

大众汽车

大众汽车

大众汽车集团是中国汽车工业最早、最成功的国际合作伙伴之一,伴随中国汽车工业成长近四十年。大众汽车集团(中国)始终致力于成为中国社会最值得信赖的合作伙伴,在为消费者提供可靠、高质量产品与服务的同时,更以支持中国社会的可持续发展作为重要己任。

大众汽车集团是中国汽车工业最早、最成功的国际合作伙伴之一,伴随中国汽车工业成长近四十年。大众汽车集团(中国)始终致力于成为中国社会最值得信赖的合作伙伴,在为消费者提供可靠、高质量产品与服务的同时,更以支持中国社会的可持续发展作为重要己任。收起

查看更多

相关推荐