Example 2: Expanded ADS function blocks

The VB application sends an array containing integer values to a PLC task. The most recently sent values are added to a list, and are copied into a corresponding array variable in the PLC. The VB application uses a WRITE request to send the data. The ADSWRITEIND function block is used in the PLC in order to receive the data, while the ADSWRITERES function block is used to acknowledge the WRITE request. In order to communicate with the PLC task the VB application uses the ActiveX control: AdsOcx.

Example 2: Expanded ADS function blocks 1:

Here you can unpack the complete sources relating to the example application: Example application with the expanded ADS function blocks

The VB application

A connection to the PLC task is constructed in the Form_Load-Routine (port 802 on the local computer). The desired service from the PLC task is encoded in the index group and index offset parameters. E.g.:

So that the requests can be routed to the PLC task, the highest value bit must be set in the index group parameter.

Option Explicit
    
Dim AdsResult     As Integer
Dim arrData(0 To 9)   As Integer

Private Sub cmdWrite_Click()
    Call List1.Clear
    
    Dim i As Long
    For i = LBound(arrData) To UBound(arrData)
    arrData(i) = arrData(i) + 1 'change values
    Call List1.AddItem("arrData(" & i & ") = " & arrData(i))
    Next i
    
    'calculate the byte length parameter
    Dim cbWriteSize As Long
    cbWriteSize = (UBound(arrData) - LBound(arrData) + 1) * LenB(arrData(LBound(arrData)))
    
    AdsResult = AdsOcx1.AdsSyncWriteReq(&H80000005, &H7, cbWriteSize, arrData) 'send data to PLC
    Label1.Caption = "Ads result: " & AdsResult
    
End Sub

Private Sub Form_Load()
    AdsOcx1.AdsAmsServerNetId = AdsOcx1.AdsAmsClientNetId
    AdsOcx1.AdsAmsServerPort = 802 'PLC task number'
    
    Dim i As Long
    For i = LBound(arrData) To UBound(arrData)
    arrData(i) = i    'init data
    Next i
End Sub

The PLC program

The requests are intercepted as indications in the PLC task by an instance of the ADSWRITEIND function block. Following this, the index group, index offset and transmitted data length parameters are checked for validity, and the desired operation is carried out on the PLC variable. The next step is for a response to be returned to the caller (including an error code, if appropriate) by an instance of the ADSWRITERES function block. At the end the CLEAR and RESPOND flags are reset in order to be able to process further indications.

Example 2: Expanded ADS function blocks 2:

With the rising edge at the CLEAR input of the ADSWRITEIND function block the address pointer to the most recently sent data becomes invalid ( == NULL ). For this reason the sent data is first copied into the PLC variable before the CLEAR input is set to TRUE.

 

PROGRAM MAIN
VAR
    fbWriteInd      : ADSWRITEIND;
    fbWriteRes      : ADSWRITERES;

    sNetId      : T_AmsNetID;
    nPort       : T_AmsPort;
    nInvokeId       : UDINT;
    nIdxGrp     : UDINT;
    nIdxOffs        : UDINT;
    cbWrite     : UDINT;(* Byte size of written data *)
    pWrite      : POINTER TO BYTE;(* Pointer to written data buffer *)
    nResult     : UDINT;(* Write indication result error code *)

    arrInt      : ARRAY[0..9] OF INT;(* Server data *)
END_VAR

 

fbWriteRes( RESPOND := FALSE );(* Reset response function block *)
fbWriteInd( CLEAR := FALSE );(* Trigger indication function block *)
IF ( fbWriteInd.VALID ) THEN
    
    sNetId      := fbWriteInd.NETID;
    nPort       := fbWriteInd.PORT;
    nInvokeId       := fbWriteInd.INVOKEID;
    nIdxGrp         := fbWriteInd.IDXGRP;
    nIdxOffs        := fbWriteInd.IDXOFFS;

    cbWrite     := fbWriteInd.LENGTH;
    pWrite      := fbWriteInd.DATAADDR;     
    nResult     := 16#701;(* ADS error: Service not supported by server *)

    CASE nIdxGrp OF
        16#80000005:
            CASE nIdxOffs OF
                16#00000007:
                    IF  cbWrite <= SIZEOF( arrInt ) THEN
                        MEMCPY( ADR( arrInt ), pWrite, MIN( cbWrite, SIZEOF(arrInt) ) );
                        nResult := 0;
                    ELSE(* ADS error (example): Invalid size *)
                        nResult := 16#705;
                    END_IF
            ELSE(* ADS error (example): Invalid index offset *)
                nResult := 16#703;
            END_CASE
    ELSE(* ADS error (example): Invalid index group *)
        nResult := 16#702;
    END_CASE

    fbWriteRes( NETID := sNetId,
                PORT := nPort,
                INVOKEID := nInvokeId,
                RESULT := nResult,
                RESPOND :=  TRUE ); (* Send write response *)

    fbWriteInd( CLEAR := TRUE ); (* Clear indication entry *)
END_IF

 

Here you can unpack the complete sources relating to the example application: Example application with the expanded ADS function blocks