Device diagnosis ( EtherCAT IO card )

andre-luis
2025-02-06
2025-07-15
  • andre-luis

    andre-luis - 2025-02-06

    Hi,

    In our project, it is not rare to have I/O cards issues.
    I wanted to have a way to check their statuses ( Eg. INIT, OP, PREOP, etc.. )

    I was trying to use the GetDeviceDiagnosisInfo method from the IoConfig_Globals, but there is no info available on the Web with a simple example; does someone have a link to some tutorial or anything else ?

    Kind Regards,
    Andre.

     
  • TimvH

    TimvH - 2025-02-06

    You can get the state by just using the name of your node (same name as in device tree):

    VAR
        etcState: IoDrvEthercatDriverLib.ETC_SLAVE_STATE;
    END_VAR
    
    etcState := EJ1100.wState;
    

    Alternatively, you can iterate through the device tree to get the status of all (Ethercat) nodes. See here an example (no guarantee it works, just give as suggestion):

    PROGRAM P_IO_State
    VAR CONSTANT
        uiMAX_NR_OF_TERMINALS : UINT := 100;
    END_VAR
    VAR
        // general device diagnosis
        // can only be used when the Device - PLC Settings - Advanced settings - enable diagnosis for devices is enabled
        // this will add the DED library
        coupler: DED.INode;
        terminal: DED.INode;
        aDeviceState: ARRAY[0..uiMAX_NR_OF_TERMINALS - 1] OF DED.DEVICE_STATE;
    
        uiTerminalCount : UINT;
        uiIndex : UINT;
    
        // for each terminal check if it implements device diagnostics
        xQueryResult: BOOL;
        itfDevice : DED.IDevice2;
        xDiagAvailable: BOOL;
        eError: DED.ERROR;
    
        xEverythingOK: BOOL;
    END_VAR
    
    uiTerminalCount := 0;
    // start at the top of the device tree with the first EtherCAT coupler
    coupler := EtherCAT_Master.FirstChildNode;
    WHILE coupler <> 0 DO
        // for each coupler that is found start at the first terminal
        terminal := coupler.FirstChildNode;
        WHILE terminal <> 0 AND uiTerminalCount < uiMAX_NR_OF_TERMINALS - 1 DO
            // for each terminal that is found get the status through the device diagnosis interface
            xQueryResult := __QUERYINTERFACE(terminal, itfDevice);
            IF xQueryResult THEN
                aDeviceState[uiTerminalCount] := itfDevice.GetDeviceState(xDiagnosisInfoAvailable => xDiagAvailable, eError => eError);
                uiTerminalCount := uiTerminalCount + 1;
            END_IF
            terminal := terminal.NextSiblingNode;
        END_WHILE
        coupler := coupler.NextSiblingNode;
    END_WHILE
    
    uiIndex := 0;
    xEverythingOK := TRUE;
    WHILE uiIndex < uiTerminalCount DO
        IF aDeviceState[uiIndex] <> DED.DEVICE_STATE.RUNNING THEN
            xEverythingOK := FALSE;
            EXIT;
        END_IF
        uiIndex := uiIndex + 1;
    END_WHILE
    
     
  • andre-luis

    andre-luis - 2025-02-06

    Hi @TimvH,

    In the meantime, I did some experiments and could find a way to achieve similar result.
    I made some tests, removing the last card from the array, seeing the change on status.

    PROGRAM P_IOS_STATUSES
    VAR
        devStateInput               :   DEVICE_STATE;
        devStateOutput              :   DEVICE_STATE;
        devStateEncoder             :   DEVICE_STATE;
    END_VAR
        devStateInput   :=  IoConfig_Globals.EL1819.GetDeviceState();
        devStateOutput  :=  IoConfig_Globals.EL2809.GetDeviceState();
        devStateEncoder :=  IoConfig_Globals.EL5151.GetDeviceState();
    
        GVL.bInputCardOK        :=  ( devStateInput = DEVICE_STATE.RUNNING );
        GVL.bOutputCardOK       :=  ( devStateOutput = DEVICE_STATE.RUNNING );
        GVL.bEncoderCardOK      :=  ( devStateEncoder = DEVICE_STATE.RUNNING );
    

    Many thanks for your quick and extensive reply.

     
  • jonasz - 2025-07-15

    Hi,
    I'll link to the topic not wanting to start a new one.
    In the application I am building, I wanted to use the diagnostics described in the CAA Device Diagnosis library.
    In principle, everything is ok, except for the elements related to ModbusTCP.
    Despite the fact that ModbusTCP is taken into account in the documentation, it is not recognised via the interfaces.

    FUNCTION_BLOCK NET_HW_DIAG
    VAR_INPUT
    END_VAR
    VAR_OUTPUT
    END_VAR
    VAR
        (* Referencja do struktury z danymi dla HMI ASTRAADA.*)
            Visu: REFERENCE TO VISU;
    
        (* Wskaźnik na mastera EtherCAT.*)
            pEtherCATMaster : POINTER TO IoDrvEtherCAT;
        (* Wskaźnik na slave EtherCAT.*)
            pEtherCATSlave : POINTER TO EtcSlave;
    
        (* Interfejs dla węzła w drzewie urządzeń.*)
            _itfNode: DED.INode;
        (* Ogólny interfejs magistrali. Zapewnia podstawowe informacje o magistrali polowej.*)
            _itfBus: DED.IBus;
        (* Interfejs urzÄ…dzenia. Zapewnia rozszerzone informacje o urzÄ…dzeniu (magistralowym).*)
            _itfDevice2: DED.IDevice2;
        (* Ogólny interfejs magistrali. Zapewnia podstawowe informacje o magistrali polowej.*)
            _itfStack: DED.IStack;
    
    
        (* Numer węzła w drzewie urządzeń.*)
            uiNodes: UINT;
        (*
            Operator jest rozszerzeniem normy IEC 61131-3.
            W czasie wykonywania operator wykonuje konwersję typu odwołania do interfejsu na inny typ.
            Operator zwraca wynik BOOL. Wartość TRUE oznacza, że CODESYS pomyślnie wykonał konwersję.
        *)
            xQueryResultBus: BOOL;
            xQueryResultDevice2: BOOL;
            xQueryResultStack: BOOL;
    
        (* Struktura danych dotyczących węzłów w drzewie urządzeń.*)
            NetHwDiag: NW_HW_STAT;
    
        ModbusTcpClientDeviceInfo: IoDrvModbusTCP.DED.DEVICE_INFO;
        ModbusTcpClientDeviceState: IoDrvModbusTCP.DED.DEVICE_STATE;
        ModbusTcpDeviceInfo: IoDrvModbusTCP.DED.DEVICE_INFO;
        ModbusTcpDeviceState: IoDrvModbusTCP.DED.DEVICE_STATE;
    
    END_VAR
    
    (* Pobranie wskaźników dla pierwszego mastera i pierwszego slave w sieci EtherCAT.*)
        pEtherCATMaster := g_pFirstMaster;
        pEtherCATSlave := pEtherCATMaster^.FirstSlave;
    
    (* Diagnostyka sieci EtherCAT.*)
        pEtherCATMaster := g_pFirstMaster;
        pEtherCATSlave := pEtherCATMaster^.FirstSlave;
        NetHwDiag.xConfigFinished := pEtherCATMaster^.xConfigFinished;
        NetHwDiag.xDistributedClockInSync := pEtherCATMaster^.xDistributedClockInSync;
        NetHwDiag.xError := pEtherCATMaster^.xError;
        NetHwDiag.xSyncInWindow := pEtherCATMaster^.xSyncInWindow;
        NetHwDiag.sLastMessage := pEtherCATMaster^.LastMessage;
        NetHwDiag.LastError := pEtherCATMaster^.LastError;
    
    (* Diagnostyka drzewa urządzeń.*)
        uiNodes := 0;
        _itfNode := DED.GetRoot();
    
        REPEAT
            NetHwDiag.asDeviceName[uiNodes] := DED.GetDeviceNameString(itfNode := _itfNode);
    
    
            xQueryResultBus := __QUERYINTERFACE(_itfNode,_itfBus);
            IF xQueryResultBus THEN
                _itfBus.GetBusInfo(buiInfo := NetHwDiag.aBusInfo[uiNodes]);
                NetHwDiag.aBusState[uiNodes] := _itfBus.GetBusState();
            END_IF
    
            xQueryResultDevice2 := __QUERYINTERFACE(_itfNode,_itfDevice2);
            IF xQueryResultDevice2 THEN
                _itfDevice2.GetDeviceInfo(deiInfo := NetHwDiag.aDeviceInfo[uiNodes]);
                NetHwDiag.aDeviceState[uiNodes] := _itfDevice2.GetDeviceState();
                IF pEtherCATSlave <>0 THEN
                    pEtherCATSlave^();
                    IF pEtherCATSlave^.SlaveAddr = NetHwDiag.aDeviceInfo[uiNodes].idSystem THEN
                        NetHwDiag.aAlStatus[uiNodes] := pEtherCATSlave^.ALStatus;
                        pEtherCATSlave := pEtherCATSlave^.NextInstance;
                    END_IF
                END_IF
            ELSE
                xQueryResultStack := __QUERYINTERFACE(_itfNode,_itfStack);
                IF xQueryResultStack THEN
                    _itfStack.GetDeviceInfo(deiInfo := NetHwDiag.aDeviceInfo[uiNodes]);
                    NetHwDiag.aDeviceState[uiNodes] := _itfStack.GetDeviceState();
                END_IF
            END_IF
    
            uiNodes := uiNodes + 1;
            _itfNode := DED.GetNextNode(_itfNode);
        UNTIL
            _itfNode = 0
        END_REPEAT
    
    (* Diagnostyka Modbus.*)
        Modbus_TCP_Client.GetDeviceInfo(deiInfo := ModbusTcpClientDeviceInfo);
        ModbusTcpClientDeviceState := Modbus_TCP_Client.GetDeviceState();
        PAC_3200T.GetDeviceInfo(deiInfo := ModbusTcpDeviceInfo);
        ModbusTcpDeviceState := PAC_3200T.GetDeviceState(); 
    

    Of course, you can take the easy way out and refer directly to the devices, but I wanted a reusable component.

    Any constructive help is very welcome
    Best regards
    Jonasz

     

Log in to post a comment.