Showing posts with label utilities. Show all posts
Showing posts with label utilities. Show all posts

Friday, September 10, 2010

WebDynpro: Progress indicator (similar to SAPGUI_PROGRESS_INDICATOR)

Within normal ABAP reporting, if we are doing some long queries or posting a lot of transactions we would use the FM SAPGUI_PROGRESS_INDICATOR to stop the report from timing out. The same scenario can happen in WebDynpro as well. By default a WebDynpro application times out in 5 minutes. So posting a lot of transaction becomes a problem here too. WebDynpro does not provide a similar function to SAPGUI_PROGRESS_INDICATOR yet (coming in 7.1?).

So we would have to implement our own progress indicator by creating a WebDynpro component using UI elements TimedTrigger and ProgressIndicator. UI element TimedTrigger is an invisible element which when placed on a view, triggers an event every given number of seconds. This is an expensive UI element to have as it creates additional burden on the server, but we have no choice so we will use this. UI element ProgressIndicator is used to show a progress bar. So the idea is to split big job into chunks that can be done in small amount of time (less than the time out period). Every time TimedTrigger fires an event we process the next available chunk of work and set the progress bar and so on.

The calling WD component will use this component for displaying the progress bar and getting a timed wake up call. The calling WD will already have the dataset ready. It would decide how many chunks the dataset will be needed divide into. So once the user triggers the action this progress indicator (PI) WD component is called in a dialog window. The calling WD subscribes to the PI WD event of timed trigger so that it wakes up to send next chunk of data. Using the assistance class of PI WD the calling WD will set the percentage of data completion as well so that the progress bar will keep on increasing.

Let’s see what we need to create in this PI WD component:

clip_image002 UI elements in the view

clip_image004 Layout

image Components

image Context

image Events

Methods of the Component Controller

ACTION_KEEP_ALIVE call from timed trigger
GET_MODEL Return the model class
INIT_PROG_VIEW Initialise progress view
RAISE_WINDOW_CLOSE Raise event for window closed
SET_PERCENTAGE Set percentage complete
SET_PROGRESS_MSG Set progress message
ACTION_KEEP_ALIVE
  1. METHOD action_keep_alive .
  2. set_percentage( ).
  3. wd_this->fire_keep_alive_evt( ).


GET_MODEL
  1. METHOD get_model .
  2. r_o_model = wd_assist.


INIT_PROG_VIEW
  1. method INIT_PROG_VIEW .
  2. wd_assist->set_percent_comp( 0 ).
  3. wd_this->set_percentage( ).
  4. wd_this->set_progress_msg( ).


RAISE_WINDOW_CLOSE
  1. METHOD raise_window_close .
  2. wd_this->fire_window_close_evt(
  3. ).


SET_PERCENTAGE
  1. METHOD set_percentage .
  2. DATA lo_nd_progress TYPE REF TO if_wd_context_node.
  3. DATA lo_el_progress TYPE REF TO if_wd_context_element.
  4. DATA ls_progress TYPE wd_this->element_progress.
  5. DATA lv_percent LIKE ls_progress-percent.
  6. * navigate from <CONTEXT> to <PROGRESS> via lead selection
  7. lo_nd_progress = wd_context->get_child_node( name = wd_this->wdctx_progress ).
  8. * get element via lead selection
  9. lo_el_progress = lo_nd_progress->get_element( ).
  10. lv_percent = wd_assist->get_percent_comp( ).
  11. * get single attribute
  12. lo_el_progress->set_attribute(
  13. EXPORTING
  14. name = `PERCENT`
  15. value = lv_percent ).


SET_PROGRESS_MSG
  1. METHOD set_progress_msg .
  2. DATA lo_nd_progress TYPE REF TO if_wd_context_node.
  3. DATA lo_el_progress TYPE REF TO if_wd_context_element.
  4. DATA ls_progress TYPE wd_this->element_progress.
  5. DATA lv_message LIKE ls_progress-message.
  6. * navigate from <CONTEXT> to <PROGRESS> via lead selection
  7. lo_nd_progress = wd_context->get_child_node( name = wd_this->wdctx_progress ).
  8. * get element via lead selection
  9. lo_el_progress = lo_nd_progress->get_element( ).
  10. lv_message = wd_assist->get_progress_msg( ).
  11. * get single attribute
  12. lo_el_progress->set_attribute(
  13. EXPORTING
  14. name = `MESSAGE`
  15. value = lv_message ).
The assistance class has these methods:

GET_PROGRESS_MSG

Get the progress message
SET_PROGRESS_MSG Set the message in the progress bar
SET_PERCENT_COMP Returns the percentage done
GET_PERCENT_COMP Returns the percentage done


These are just getter and setter methods for the filling the Context variables. The calling WD component can set the percentage and message to be displayed. The PI WD will read these values and set in the context.

Usage

Let’s look at the usage of the progress indicator in a example WD component. Create a new WD where we will use the PI WD component. Add the PI WD component in the properties.

image

In the component controller properties add it as used component comptroller.

image

Subscribe to the events of the PI WD component.

image

This catches events for timed trigger and window close in case user cancels before 100% completion of the process.

CATCH_TIMED_TRIGGER
  1. METHOD catch_timed_trigger .
  2. DATA l_percentage TYPE i.
  3. l_percentage = wd_this->o_zcl_rc_library->get_percent_comp( ).
  4. IF l_percentage >= 100.
  5. wd_this->o_progress_window->close( ).
  6. ENDIF.
  7. l_percentage = l_percentage + 10.
  8. wd_this->o_zcl_rc_library->set_percent_comp( l_percentage ).
  9. ENDMETHOD.
We are here just incrementing the percentage by 10% each time the trigger is activated. But in real situation one would increment to the next dataset that is planned to be processed. The percentage will be according to that.

CATCH_WINDOW_CLOSE
  1. METHOD catch_window_close .
  2. * Check action is over because it is finished or
  3. * killed in between
  4. CHECK wd_this->flag_action_start = abap_true. " Action is killed
  5. " ERROR
  6. * get message manager
  7. DATA lo_api_controller TYPE REF TO if_wd_controller.
  8. DATA lo_message_manager TYPE REF TO if_wd_message_manager.
  9. lo_api_controller ?= wd_this->wd_get_api( ).
  10. lo_message_manager = lo_api_controller->get_message_manager( ).
  11. * report message
  12. lo_message_manager->report_error_message(
  13. message_text = 'Window cancelled before process completion'
  14. ).
  15. * Clear variables
  16. CASE wd_this->action.
  17. WHEN 1. " Validate
  18. WHEN 2. " Action
  19. WHEN OTHERS.
  20. ENDCASE.
  21. ENDMETHOD.
If the progress window is closed before all data has been processed you can alert the user that with that error.

Let’s look at the component controllers methods.


WDDOINIT
  1. METHOD wddoinit .
  2. DATA lo_cmp_usage TYPE REF TO if_wd_component_usage.
  3. lo_cmp_usage = wd_this->wd_cpuse_zwdrc_progress_window( ).
  4. IF lo_cmp_usage->has_active_component( ) IS INITIAL.
  5. lo_cmp_usage->create_component( ).
  6. ENDIF.
  7. DATA lo_interfacecontroller TYPE REF TO ziwci_wdrc_progress_window .
  8. lo_interfacecontroller = wd_this->wd_cpifc_zwdrc_progress_window( ).
  9. wd_this->o_zcl_rc_library = lo_interfacecontroller->get_model( ).
  10. ENDMETHOD.
This will initialise the PI WD component and get the assistance class handler.

We have a simple example which has a button that will launch the progress window.

image

The action attached to the button is:

ONACTIONCALL_PROGRESS
  1. METHOD onactioncall_progress .
  2. wd_comp_controller->open_progress_window( ).
  3. ENDMETHOD.
Here the real application would start the working of whatever transaction was planned. The progress window is further opened as:

OPEN_PROGRESS_WINDOW
  1. METHOD open_progress_window .
  2. DATA lo_componentinterface TYPE REF TO if_wd_component_usage.
  3. lo_componentinterface = wd_this->wd_cpuse_zwdrc_progress_window( ).
  4. DATA lo_interface TYPE REF TO ziwci_wdrc_progress_window.
  5. lo_interface = wd_this->wd_cpifc_zwdrc_progress_window( ).
  6. wd_this->o_zcl_rc_library->set_progress_msg( 'We are doing it ').
  7. wd_this->o_zcl_rc_library->set_percent_comp( 0 ).
  8. DATA lo_cont TYPE REF TO if_wd_controller.
  9. * if_wd_controller = lo_interface->wd_get_api( ).
  10. DATA lo_api_componentcontroller TYPE REF TO if_wd_component.
  11. lo_api_componentcontroller = wd_this->wd_get_api( ).
  12. DATA l_window_manager TYPE REF TO if_wd_window_manager.
  13. l_window_manager = lo_api_componentcontroller->get_window_manager( ).
  14. wd_this->o_progress_window = l_window_manager->create_window_for_cmp_usage(
  15. interface_view_name = 'W_MAIN'
  16. component_usage_name = 'ZWDRC_PROGRESS_WINDOW'
  17. title = 'We are doing it'
  18. * close_in_any_case = ABAP_TRUE
  19. * message_display_mode = message_display_mode
  20. ).
  21. * wd_this->o_progress_window->set_window_title( title = 'Test' ).
  22. wd_this->o_progress_window->open( ).
  23. ENDMETHOD.
Here the window W_MAIN of PI WD component is being called. The title is set and so is the message that will come in the message bar.

Here is the example of the output:

image 10% done

image 20% done

Monday, December 14, 2009

Utility: Find similar strings using Levenshtein distance

This posting is basically to show the Levenshtein distance implementation in ABAP. This implementation was created to find if there is already a similar name of a given name. For example if you are searching for ‘John’ then ‘Johnny’ could also be presented in the search.

The Levenshtein distance gives the difference of characters between two strings. So in the case of ‘John’ and ‘Johnny’ the distance would be 2. The implementation is done using a class-method. The method accepts string1 and string2 and returns the distance. The program calling the method can then decided if the distance is acceptable or not.

image

METHOD get_levenshtein_distance.
  DATA lt_matrix TYPE REF TO data.
  DATA lst_matrix TYPE REF TO data.
  DATA l_str_len1 TYPE i.
  DATA l_str_len1_tmp TYPE i.
  DATA l_str_len2 TYPE i.
  DATA l_str_len2_tmp TYPE i.
  DATA l_count1 TYPE i.
  DATA l_count2 TYPE i.
  DATA l_i TYPE i.
  DATA l_j TYPE i.
  DATA l_i_1 TYPE i.
  DATA l_j_1 TYPE i.
  DATA l_field_value TYPE i.
  DATA lo_cl_abap_datadescr.
  DATA l_col_name TYPE string.
  DATA l_n_index(3) TYPE n.
  DATA lt_abap_component_tab TYPE abap_component_tab.
  DATA lst_abap_component TYPE abap_componentdescr.
  DATA lo_abap_datadescr TYPE REF TO cl_abap_datadescr.
  DATA lo_abap_structdescr TYPE REF TO cl_abap_structdescr.
  DATA lo_abap_tabledescr TYPE REF TO cl_abap_tabledescr.
  DATA lt_number TYPE TABLE OF i.
  FIELD-SYMBOLS <lt_matrix> TYPE table.
  FIELD-SYMBOLS <lst_matrix> TYPE data.
  FIELD-SYMBOLS <l_field> TYPE data.
  FIELD-SYMBOLS <l_field2> TYPE data.
* Create data type of number
  lo_abap_datadescr ?= cl_abap_datadescr=>describe_by_data( 1 ).
* Get the string lengths
  l_str_len1 = STRLEN( i_string1 ).
  l_str_len2 = STRLEN( i_string2 ).
* Create (l_str_len2 + 1) number of columns
  l_str_len2_tmp = l_str_len2 + 1.
  DO l_str_len2_tmp TIMES.
    l_n_index = sy-index.
    CONCATENATE 'COL' l_n_index INTO l_col_name.
    lst_abap_component-name = l_col_name.
    lst_abap_component-type = lo_abap_datadescr.
    APPEND lst_abap_component TO lt_abap_component_tab.
  ENDDO.
  lo_abap_structdescr = cl_abap_structdescr=>create( p_components = lt_abap_component_tab ).
  lo_abap_tabledescr = cl_abap_tabledescr=>create( p_line_type = lo_abap_structdescr ).
  CREATE DATA lt_matrix TYPE HANDLE lo_abap_tabledescr.
  ASSIGN lt_matrix->* TO <lt_matrix>.
  CREATE DATA lst_matrix TYPE HANDLE lo_abap_structdescr.
  ASSIGN lst_matrix->* TO <lst_matrix>.
* Initialise first row
  WHILE sy-subrc IS INITIAL.
    ASSIGN COMPONENT sy-index OF STRUCTURE <lst_matrix> TO <l_field>.
    IF sy-subrc IS NOT INITIAL.
      EXIT.
    ENDIF.
    <l_field> = sy-index - 1.
  ENDWHILE.
  APPEND <lst_matrix> TO <lt_matrix>.
* Create (l_str_len1 + 1) number of rows
* First row is already created
  DO l_str_len1 TIMES.
    CLEAR <lst_matrix>.
    ASSIGN COMPONENT 1 OF STRUCTURE <lst_matrix> TO <l_field>.
    <l_field> = sy-index.
    APPEND <lst_matrix> TO <lt_matrix>.
  ENDDO.
  l_j = 2. " Column
  DO l_str_len2 TIMES.
    CLEAR l_count1.
    l_i = 2. " Row
    DO l_str_len1 TIMES.
* i - 1
      l_i_1 = l_i - 1.
* j - 1
      l_j_1 = l_j - 1.
* Get d[i, j]
      READ TABLE <lt_matrix> INDEX l_i ASSIGNING <lst_matrix>.
      ASSIGN COMPONENT l_j OF STRUCTURE <lst_matrix> TO <l_field2>.
      IF i_string1+l_count1(1) = i_string2+l_count2(1).
* Get d[i-1, j-1]
        READ TABLE <lt_matrix> INDEX l_i_1 ASSIGNING <lst_matrix>.
        ASSIGN COMPONENT l_j_1 OF STRUCTURE <lst_matrix> TO <l_field>.
*         d[i, j] := d[i-1, j-1]
        <l_field2> = <l_field>.
      ELSE.
        CLEAR lt_number[].
*                      d[i-1, j] + 1,  // deletion
        UNASSIGN <l_field>.
        READ TABLE <lt_matrix> INDEX l_i_1 ASSIGNING <lst_matrix>.
        ASSIGN COMPONENT l_j OF STRUCTURE <lst_matrix> TO <l_field>.
        l_field_value = <l_field> + 1.
        APPEND l_field_value TO lt_number.
*                      d[i, j-1] + 1,  // insertion
        UNASSIGN <l_field>.
        READ TABLE <lt_matrix> INDEX l_i ASSIGNING <lst_matrix>.
        ASSIGN COMPONENT l_j_1 OF STRUCTURE <lst_matrix> TO <l_field>.
        l_field_value = <l_field> + 1.
        APPEND l_field_value TO lt_number.
*                      d[i-1, j-1] + 1 // substitution
        UNASSIGN <l_field>.
        READ TABLE <lt_matrix> INDEX l_i_1 ASSIGNING <lst_matrix>.
        ASSIGN COMPONENT l_j_1 OF STRUCTURE <lst_matrix> TO <l_field>.
        l_field_value = <l_field> + 1.
        APPEND l_field_value TO lt_number.
* Get the minimum
        UNASSIGN <l_field>.
        SORT lt_number.
        READ TABLE lt_number INDEX 1 ASSIGNING <l_field>.
*         d[i, j] := minimum
        <l_field2> = <l_field>.
      ENDIF.
      l_i = l_i + 1.
      l_count1 = l_count1 + 1.
    ENDDO.
    l_j = l_j + 1.
    l_count2 = l_count2 + 1.
  ENDDO.
* Bottom right of the array lt_matrix will have the answer
  l_str_len1_tmp = l_str_len1 + 1.
  l_str_len2_tmp = l_str_len2 + 1.
  READ TABLE <lt_matrix> INDEX l_str_len1_tmp ASSIGNING <lst_matrix>.
  ASSIGN COMPONENT l_str_len2_tmp OF STRUCTURE <lst_matrix> TO <l_field>.
  r_distance = <l_field>.
ENDMETHOD.

Wednesday, October 14, 2009

Update to Upload program

Have modified the upload program to make it similar to the recent download program.

*&---------------------------------------------------------------------*
*& Report  ZUPLOAD
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT ZUPLOAD MESSAGE-ID zx_message.
*----------------------------------------------------------------------*
*       CLASS upload DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS upload DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS open_file.
    METHODS start.
    METHODS end.
    METHODS free_log.
    METHODS display_output.
    METHODS display_log.
  PRIVATE SECTION.
    CONSTANTS c_max_ranges TYPE i VALUE 100.
    CONSTANTS c_seperator TYPE abap_char1 VALUE cl_abap_char_utilities=>horizontal_tab.
    DATA upload_file_tab TYPE REF TO data.
    DATA upload_file_row TYPE REF TO data.
    DATA t_tab_file TYPE TABLE OF string.
    DATA o_struc_type TYPE REF TO cl_abap_structdescr.
    DATA s_log TYPE bal_s_log.
    DATA log_handle         TYPE balloghndl.
    DATA t_log_handle       TYPE bal_t_logh.
    DATA o_ccont_display TYPE REF TO cl_gui_custom_container.
    DATA o_ccont_log TYPE REF TO cl_gui_custom_container.
    DATA dummy.
    DATA t_filecontent TYPE string_table.
    METHODS convert_file2struc IMPORTING i_header TYPE boolean
                                         i_separator TYPE c.
    METHODS create_table_and_row.
    METHODS read_file IMPORTING i_file TYPE string.
    METHODS open_log.
    METHODS add_msg.
ENDCLASS.                    "upload DEFINITION
DATA o_upload TYPE REF TO upload.
DATA s_t001 TYPE t001.
*--------------------------------------------------------------------*
* File Location
SELECTION-SCREEN BEGIN OF BLOCK file WITH FRAME TITLE text-f01.
PARAMETERS:
  p_file   TYPE rlgrap-filename OBLIGATORY,
  p_header AS CHECKBOX DEFAULT abap_true.
SELECTION-SCREEN END OF BLOCK file.
*--------------------------------------------------------------------*
* Material selection
SELECTION-SCREEN BEGIN OF BLOCK bukrs WITH FRAME TITLE text-001.
*--- company options
SELECT-OPTIONS s_bukrs FOR s_t001-bukrs.
SELECTION-SCREEN END OF BLOCK bukrs.
*--------------------------------------------------------------------*
* Defaults
SELECTION-SCREEN BEGIN OF BLOCK intr WITH FRAME TITLE text-t03.
PARAMETERS:
  p_struc TYPE strukname DEFAULT 'T001'.
SELECTION-SCREEN END OF BLOCK intr.
*--------------------------------------------------------------------*
* F4
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
  upload=>open_file( ).
*--------------------------------------------------------------------*
START-OF-SELECTION.
  CREATE OBJECT o_upload.
  o_upload->start( ).
*--------------------------------------------------------------------*
END-OF-SELECTION.
  o_upload->end( ).
  CALL SCREEN 0100.
*----------------------------------------------------------------------*
*       CLASS upload IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS upload IMPLEMENTATION.
  METHOD open_file.
    DATA:
        l_t_file_table TYPE TABLE OF file_table,
        l_s_file_table TYPE file_table.
    DATA l_subrc TYPE i.
    CALL METHOD cl_gui_frontend_services=>file_open_dialog
      CHANGING
        file_table              = l_t_file_table
        rc                      = l_subrc
      EXCEPTIONS
        file_open_dialog_failed = 1
        cntl_error              = 2
        error_no_gui            = 3
        not_supported_by_gui    = 4
        OTHERS                  = 5.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
    READ TABLE l_t_file_table INTO l_s_file_table INDEX 1.
    p_file = l_s_file_table-filename.
  ENDMETHOD.                    "open_file
  METHOD start.
    DATA l_file TYPE string.
    l_file = p_file.
    open_log( ).
    create_table_and_row( ).
    read_file( i_file = l_file ).
    convert_file2struc( i_header    = p_header
                        i_separator = c_seperator ).
  ENDMETHOD.                    "start
  METHOD create_table_and_row.
    DATA l_type TYPE string.
    DATA lo_type TYPE REF TO cl_abap_typedescr.
    DATA lo_tabletype TYPE REF TO cl_abap_tabledescr.
    l_type = p_struc.
    CALL METHOD cl_abap_structdescr=>describe_by_name
      EXPORTING
        p_name         = l_type
      RECEIVING
        p_descr_ref    = lo_type
      EXCEPTIONS
        type_not_found = 1
        OTHERS         = 2.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
    o_struc_type ?= lo_type.
    CREATE DATA upload_file_row TYPE HANDLE o_struc_type.
    TRY.
        CALL METHOD cl_abap_tabledescr=>create
          EXPORTING
            p_line_type  = o_struc_type
*    P_TABLE_KIND = TABLEKIND_STD
*    P_UNIQUE     = ABAP_FALSE
*    P_KEY        =
*    P_KEY_KIND   = KEYDEFKIND_DEFAULT
          RECEIVING
            p_result     = lo_tabletype
            .
      CATCH cx_sy_table_creation .
    ENDTRY.
    CREATE DATA upload_file_tab TYPE HANDLE lo_tabletype.
* Structure mapped successfully
    MESSAGE s181 INTO dummy.
    add_msg( ).
  ENDMETHOD.                    "create_table_and_row
  METHOD convert_file2struc.
    DATA:
      l_struc TYPE REF TO data,
      l_o_datadescr TYPE REF TO cl_abap_datadescr,
      l_o_tabledescr TYPE REF TO cl_abap_tabledescr,
      l_t_fields TYPE string_table,
      l_s_fields TYPE string.
    FIELD-SYMBOLS:
      <l_filecontent> TYPE ANY,
      <l_field> TYPE ANY,
      <l_row> TYPE ANY,
      <l_struc_content> TYPE table.
    ASSIGN upload_file_row->* TO <l_row>.
    ASSIGN upload_file_tab->* TO <l_struc_content>.
    LOOP AT t_filecontent ASSIGNING <l_filecontent>.
      IF i_separator IS INITIAL. " File is same as structure
        <l_row> = <l_filecontent>.
      ELSE.
*---split based on the separator
        SPLIT <l_filecontent> AT i_separator INTO TABLE l_t_fields.
        LOOP AT l_t_fields INTO l_s_fields.
          ASSIGN COMPONENT sy-tabix OF STRUCTURE <l_row> TO <l_field>.
          <l_field> = l_s_fields.
        ENDLOOP.
      ENDIF.
      APPEND <l_row> TO <l_struc_content>.
    ENDLOOP.
  ENDMETHOD.                    "convert_file2struc
  METHOD end.
  ENDMETHOD.                    "end
  METHOD open_log.
* create a log
    s_log-extnumber  = 'Application Log in Subscreen'(001).
    CALL FUNCTION 'BAL_LOG_CREATE'
      EXPORTING
        i_s_log      = s_log
      IMPORTING
        e_log_handle = log_handle
      EXCEPTIONS
        OTHERS       = 1.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
    INSERT log_handle INTO TABLE t_log_handle.
* Log opened successfully
    MESSAGE s180 INTO dummy.
    add_msg( ).
  ENDMETHOD.                    "open_log
  METHOD add_msg.
    DATA:
      l_s_msg   TYPE bal_s_msg.
* define data of message for Application Log
    l_s_msg-msgty     = sy-msgty.
    l_s_msg-msgid     = sy-msgid.
    l_s_msg-msgno     = sy-msgno.
    l_s_msg-msgv1     = sy-msgv1.
    l_s_msg-msgv2     = sy-msgv2.
    l_s_msg-msgv3     = sy-msgv3.
    l_s_msg-msgv4     = sy-msgv4.
* add this message to log file
    CALL FUNCTION 'BAL_LOG_MSG_ADD'
      EXPORTING
        i_log_handle = log_handle
        i_s_msg      = l_s_msg
      EXCEPTIONS
        OTHERS       = 1.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
  ENDMETHOD.                    "add_msg
  METHOD free_log.
* free all data (this must NOT be forgotten !)
    CALL FUNCTION 'BAL_DSP_OUTPUT_FREE'
      EXCEPTIONS
        OTHERS = 1.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
  ENDMETHOD.                    "free_log
  METHOD display_output.
    DATA:
      l_r_table   TYPE REF TO cl_salv_table,
*      l_r_event_handler TYPE REF TO l_cl_event_handler,
      l_r_events TYPE REF TO cl_salv_events_table,
      l_t_salv_t_int4_column TYPE salv_t_int4_column,
      l_s_salv_t_int4_column TYPE LINE OF salv_t_int4_column.
    FIELD-SYMBOLS <l_upload_file_tab> TYPE table.
    ASSIGN upload_file_tab->* TO <l_upload_file_tab>.
*--------------------------------------------------------------------*
    IF o_ccont_display IS INITIAL.
* Create holder container
      CREATE OBJECT o_ccont_display
        EXPORTING
*    PARENT                      =
          container_name              = 'CCONTROL_0101'
*    STYLE                       =
*    LIFETIME                    = lifetime_default
*    REPID                       =
*    DYNNR                       =
*    NO_AUTODEF_PROGID_DYNNR     =
        EXCEPTIONS
          cntl_error                  = 1
          cntl_system_error           = 2
          create_error                = 3
          lifetime_error              = 4
          lifetime_dynpro_dynpro_link = 5
          OTHERS                      = 6
          .
      IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                   WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      ENDIF.
*--------------------------------------------------------------------*
* Create AVL table
      TRY.
          cl_salv_table=>factory(
            EXPORTING
              r_container = o_ccont_display
            IMPORTING
              r_salv_table = l_r_table
            CHANGING
              t_table      = <l_upload_file_tab> ).
        CATCH cx_salv_msg.                              "#EC NO_HANDLER
      ENDTRY.
*--------------------------------------------------------------------*
* Functions
      DATA:
        lr_functions TYPE REF TO cl_salv_functions_list.
      lr_functions = l_r_table->get_functions( ).
*  lr_functions->set_aggregation_total( abap_true ).
      lr_functions->set_default( abap_true ).
* Columns
      DATA:
        lr_columns TYPE REF TO cl_salv_columns_table,
        lr_column TYPE REF TO cl_salv_column.
      lr_columns = l_r_table->get_columns( ).
      lr_columns->set_optimize( abap_true ).
      TRY.
          CALL METHOD lr_columns->set_exception_column
            EXPORTING
              value     = 'TRAFFIC_LIGHT'
              group     = '2'
              condensed = if_salv_c_bool_sap=>false.
        CATCH cx_salv_data_error .
      ENDTRY.
      TRY.
          CALL METHOD lr_columns->set_cell_type_column
            EXPORTING
              value = 'CELL_TYPE'.
        CATCH cx_salv_data_error .
      ENDTRY.
      l_r_table->display( ).
    ENDIF.
  ENDMETHOD.                    "get_e1maktm
  METHOD display_log.
    DATA:
      l_r_table   TYPE REF TO cl_salv_table,
*      l_r_event_handler TYPE REF TO l_cl_event_handler,
      l_r_events TYPE REF TO cl_salv_events_table,
      l_t_salv_t_int4_column TYPE salv_t_int4_column,
      l_s_salv_t_int4_column TYPE LINE OF salv_t_int4_column.
    DATA ls_display_profile  TYPE bal_s_prof.
    DATA l_control_handle     TYPE balcnthndl.
    FIELD-SYMBOLS <l_upload_file_tab> TYPE table.
*--------------------------------------------------------------------*
* Create holder container
    IF o_ccont_log IS INITIAL.
      CREATE OBJECT o_ccont_log
        EXPORTING
*    PARENT                      =
          container_name              = 'CCONTROL_0102'
*    STYLE                       =
*    LIFETIME                    = lifetime_default
*    REPID                       =
*    DYNNR                       =
*    NO_AUTODEF_PROGID_DYNNR     =
        EXCEPTIONS
          cntl_error                  = 1
          cntl_system_error           = 2
          create_error                = 3
          lifetime_error              = 4
          lifetime_dynpro_dynpro_link = 5
          OTHERS                      = 6
          .
      IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                   WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      ENDIF.
*       get a display profile which describes how to display messages
      CALL FUNCTION 'BAL_DSP_PROFILE_NO_TREE_GET'
        IMPORTING
          e_s_display_profile = ls_display_profile.
      ls_display_profile-no_toolbar = 'X'.
*--------------------------------------------------------------------*
*       create control to display data
      CALL FUNCTION 'BAL_CNTL_CREATE'
        EXPORTING
          i_container         = o_ccont_log
          i_s_display_profile = ls_display_profile
          i_t_log_handle      = t_log_handle
        IMPORTING
          e_control_handle    = l_control_handle
        EXCEPTIONS
          OTHERS              = 1.
      IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      ENDIF.
    ENDIF.
  ENDMETHOD.                    "display_log
  METHOD read_file.
    CALL METHOD cl_gui_frontend_services=>gui_upload
      EXPORTING
        filename                = i_file
*        has_field_separator     = 'X'
      CHANGING
        data_tab                = t_filecontent
      EXCEPTIONS
        file_open_error         = 1
        file_read_error         = 2
        no_batch                = 3
        gui_refuse_filetransfer = 4
        invalid_type            = 5
        no_authority            = 6
        unknown_error           = 7
        bad_data_format         = 8
        header_not_allowed      = 9
        separator_not_allowed   = 10
        header_too_long         = 11
        unknown_dp_error        = 12
        access_denied           = 13
        dp_out_of_memory        = 14
        disk_full               = 15
        dp_timeout              = 16
        not_supported_by_gui    = 17
        error_no_gui            = 18
        OTHERS                  = 19.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                 WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ENDIF.
  ENDMETHOD.                    "read_file
ENDCLASS.                    "upload IMPLEMENTATION
*&---------------------------------------------------------------------*
*&      Module  status_0100  OUTPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE status_0100 OUTPUT.
* set status and title
  SET PF-STATUS 'STATUS'.
  SET TITLEBAR 'TITLE'.
* flush data to frontend
  CALL METHOD cl_gui_cfw=>flush.
ENDMODULE.                 " status_0100  OUTPUT
*&SPWIZARD: FUNCTION CODES FOR TABSTRIP 'TAB_STRIP'
CONSTANTS: BEGIN OF c_tab_strip,
             tab1 LIKE sy-ucomm VALUE 'TAB_STRIP_FC1',
             tab2 LIKE sy-ucomm VALUE 'TAB_STRIP_FC2',
           END OF c_tab_strip.
*&SPWIZARD: DATA FOR TABSTRIP 'TAB_STRIP'
CONTROLS:  tab_strip TYPE TABSTRIP.
DATA:      BEGIN OF g_tab_strip,
             subscreen   LIKE sy-dynnr,
             prog        LIKE sy-repid VALUE 'Zupload',
             pressed_tab LIKE sy-ucomm VALUE c_tab_strip-tab1,
           END OF g_tab_strip.
DATA:      ok_code LIKE sy-ucomm.
*&SPWIZARD: OUTPUT MODULE FOR TS 'TAB_STRIP'. DO NOT CHANGE THIS LINE!
*&SPWIZARD: SETS ACTIVE TAB
MODULE tab_strip_active_tab_set OUTPUT.
  tab_strip-activetab = g_tab_strip-pressed_tab.
  CASE g_tab_strip-pressed_tab.
    WHEN c_tab_strip-tab1.
      g_tab_strip-subscreen = '0101'.
    WHEN c_tab_strip-tab2.
      g_tab_strip-subscreen = '0102'.
    WHEN OTHERS.
*&SPWIZARD:      DO NOTHING
  ENDCASE.
ENDMODULE.                    "TAB_STRIP_ACTIVE_TAB_SET OUTPUT
*&SPWIZARD: INPUT MODULE FOR TS 'TAB_STRIP'. DO NOT CHANGE THIS LINE!
*&SPWIZARD: GETS ACTIVE TAB
MODULE tab_strip_active_tab_get INPUT.
  ok_code = sy-ucomm.
  CASE ok_code.
    WHEN c_tab_strip-tab1.
      g_tab_strip-pressed_tab = c_tab_strip-tab1.
    WHEN c_tab_strip-tab2.
      g_tab_strip-pressed_tab = c_tab_strip-tab2.
    WHEN OTHERS.
*&SPWIZARD:      DO NOTHING
  ENDCASE.
ENDMODULE.                    "TAB_STRIP_ACTIVE_TAB_GET INPUT
*&---------------------------------------------------------------------*
*&      Module  user_command_0100  INPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
  CASE ok_code.
*   leave this screen
    WHEN 'ABBR' OR 'BACK' OR 'BEEN'.
      LEAVE TO SCREEN 0.
    WHEN OTHERS.
  ENDCASE.
* Delete log profile
  o_upload->free_log( ).
* call dispatch method of control framwork
  CALL METHOD cl_gui_cfw=>dispatch.
ENDMODULE.                 " user_command_0100  INPUT
*&---------------------------------------------------------------------*
*&      Module  display_output  OUTPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE display_output OUTPUT.
  o_upload->display_output( ).
ENDMODULE.                 " display_output  OUTPUT
*&---------------------------------------------------------------------*
*&      Module  display_log  OUTPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
MODULE display_log OUTPUT.
  o_upload->display_log( ).
ENDMODULE.                 " display_log  OUTPUT