Friday, 18 January 2019

Tip: How to use Memory Mapped Files with ABAP/eCATT

During my tests with the RPA tool UiPath Studio in conjunction with eCATT it seemed necessary for me to think about an effective way of exchanging data. One of the standard options of interprocess communication (IPC) in Windows environments are Memory Mapped Files (MMF). So I decided to check if it is possible to use them in this context. Here is now the second step where I describe how to use the same approach in eCATT or ABAP.

The target of my tests is to get a feeling about the integration possibilities of RPA in SAP environments like eCATT or ABAP. With MMF we can easily transfer data from one process to another and also it is very easy to transfer data of bigger amount. This approach works only in the context of the SAP GUI for Windows, because we need the possibility to use COM.

So far so well, let us start with the PowerShell script. It contains three functions to write, read and append data to MMF. We use only managed dotNET classe like  MemoryMappedFile or StreamWriter and StreamReader. Nothing mysterious. We store this PowerShell script as include development object inside our SAP system.

#-Begin-----------------------------------------------------------------

#-WriteMMF--------------------------------------------------------------
Function WriteMMF {

  Param(
    [String]$Name,
    [String]$Text
  )

  Try {

    [System.IO.MemoryMappedFiles.MemoryMappedFile]$MMF = `
      [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting($Name);
    If($Null -eq $MMF) {
      Return;
    }

    $Stream = $MMF.CreateViewStream();
    $StreamWriter = [System.IO.StreamWriter]::new($Stream);
    $StreamWriter.Write($Text);

  } Catch {
    Return;
  }

  Try {
    While($True) {
      $StreamWriter.Write([System.Convert]::ToChar(0));
    }
  } Catch {}

  Try {
    $StreamWriter.Dispose();
  } Catch {
    Return;
  }
  $Stream.Dispose();

  Write-Host "ok";

}

#-ReadMMF---------------------------------------------------------------
Function ReadMMF {

  Param(
    [String]$Name
  )

  Try {

    [System.IO.MemoryMappedFiles.MemoryMappedFile]$MMF = `
      [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting($Name);
    If($Null -eq $MMF) {
      Return;
    }

    $Stream = $MMF.CreateViewStream();
    $StreamReader = [System.IO.StreamReader]::new($Stream);
    $Text = $StreamReader.ReadToEnd().Replace("`0", "");
    $StreamReader.Dispose();

  } Catch {
    Return;
  }
  $Stream.Dispose();

  Write-Host $Text;

}

#-AppendMMF-------------------------------------------------------------
Function AppendMMF {

  Param(
    [String]$Name,
    [String]$Text
  )

  Try {

    [System.IO.MemoryMappedFiles.MemoryMappedFile]$MMF = `
      [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting($Name);
    If($Null -eq $MMF) {
      Return;
    }

    $Stream = $MMF.CreateViewStream();
    $StreamReader = [System.IO.StreamReader]::new($Stream);
    $Buffer = $StreamReader.ReadToEnd().Replace("`0", "");
    $StreamReader.Dispose();
    $Stream.Dispose();

    $Buffer = $Buffer + $Text;
    $Stream = $MMF.CreateViewStream();
    $StreamWriter = [System.IO.StreamWriter]::new($Stream);
    $StreamWriter.Write($Buffer);

  } Catch {
    Return;
  }

  Try {
    While($True) {
      $StreamWriter.Write([System.Convert]::ToChar(0));
    }
  } Catch {}

  Try {
    $StreamWriter.Dispose();
  } Catch {
    Return;
  }
  $Stream.Dispose();

  Write-Host "ok";

}

#-Main------------------------------------------------------------------
#FunctionCall

#-End-------------------------------------------------------------------

Now let us look to the ABAP class to call this PowerShell functions. It is more or less a wrapper around the PowerShell functions. We have three methods with the same arguments as the PowerShell functions. Also we have a private method to call the function. As you can see contain the methods write, read and append the PowerShell commands and with this command it executes this function from the embedded script.

"-Begin-----------------------------------------------------------------
CLASS z_cl_mmf DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS write
      IMPORTING
        VALUE(iv_mmf_name) TYPE string
        VALUE(iv_text) TYPE string
      RETURNING
        VALUE(rv_status) TYPE string.

    METHODS read
      IMPORTING
        VALUE(iv_mmf_name) TYPE string
      RETURNING
        VALUE(rv_text) TYPE string.

    METHODS append
      IMPORTING
        VALUE(iv_mmf_name) TYPE string
        VALUE(iv_text) TYPE string
      RETURNING
        VALUE(rv_status) TYPE string.

  PROTECTED SECTION.

  PRIVATE SECTION.

    METHODS call_ps_function
      IMPORTING
        VALUE(iv_functioncall) TYPE string
      RETURNING
        VALUE(rv_result)       TYPE string.

  ENDCLASS.

CLASS z_cl_mmf IMPLEMENTATION.

  METHOD write."--------------------------------------------------------

    rv_status = me->call_ps_function(
      iv_functioncall = `WriteMMF -Name ` && iv_mmf_name &&
        ` -Text '` && iv_text && `';`
    ).

  ENDMETHOD.

  METHOD read."---------------------------------------------------------

    rv_text = me->call_ps_function(
      iv_functioncall = `ReadMMF -Name ` && iv_mmf_name && `;`
    ).

  ENDMETHOD.

  METHOD append."-------------------------------------------------------

    rv_status = me->call_ps_function(
      iv_functioncall = `AppendMMF -Name ` && iv_mmf_name &&
        ` -Text '` && iv_text && `';`
    ).

  ENDMETHOD.

  METHOD call_ps_function."---------------------------------------------

    DATA:
      lo_ps     TYPE REF TO z_cl_activexposhv3,
      lv_result TYPE string,
      lv_pscode TYPE string,
      lv_temp   TYPE string
      .

    CREATE OBJECT lo_ps.

    CHECK lo_ps->load_lib( ) = lo_ps->mc_true.
    CHECK lo_ps->get_is_powershell_installed( ) = lo_ps->mc_true.
    CHECK lo_ps->init( iv_load_profiles = lo_ps->mc_false ) = 0.

    lo_ps->set_outputmode( lo_ps->mc_outputbuffer ).
    lo_ps->set_outputwidth( 132 ).
    lo_ps->clear_output( ).

    lv_pscode = lo_ps->read_incl_as_string('Z_POSH_MMF').

    REPLACE '#FunctionCall' WITH iv_functioncall INTO lv_pscode.

    lo_ps->execute( lv_pscode ).
    rv_result = lo_ps->get_outputstring( ).

    lo_ps->free_lib( ).

  ENDMETHOD.

ENDCLASS.

"-End-------------------------------------------------------------------

To try this easily we use two executables, one in the context of the frontend and another in the context of the backend. Here now a PowerShell script which initalizes a MMF and store some data in it.

#-Begin-----------------------------------------------------------------

$Text = @"
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea
rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem
ipsum dolor sit amet.
"@

#-Sub Main--------------------------------------------------------------
Function Main {

  $Name = "UiPathRobot";
  $Size = 128KB;
  [System.IO.MemoryMappedFiles.MemoryMappedFile]$MMF = `
    [System.IO.MemoryMappedFiles.MemoryMappedFile]::CreateOrOpen($Name, $Size);
  If($Null -eq $MMF) {
    Return;
  }

  $Stream = $MMF.CreateViewStream();
  $StreamWriter = [System.IO.StreamWriter]::new($Stream);
  $StreamWriter.Write($Text);
  $StreamWriter.Dispose();
  $Stream.Dispose();

  $Stream = $MMF.CreateViewStream();
  $StreamReader = [System.IO.StreamReader]::new($Stream);
  $JSON = $StreamReader.ReadToEnd().Replace("`0", "");
  $StreamReader.Dispose();
  $Stream.Dispose();

  $MMF.Dispose();

  Write-Host $JSON.Length;
  Write-Host $JSON;

}

#-Main------------------------------------------------------------------
Main;

#-End-------------------------------------------------------------------

And here an ABAP report which reads, writes and appends some data.

"-Begin-----------------------------------------------------------------
REPORT z_mmf.

  DATA:
    lo_mmf TYPE REF TO z_cl_mmf,
    lv_text TYPE string,
    lv_status TYPE string,
    lt_flight TYPE STANDARD TABLE OF sflight.

  CREATE OBJECT lo_mmf.

  lv_text = lo_mmf->read( iv_mmf_name = 'UiPathRobot' ).
  WRITE: lv_text.

  lo_mmf->write(
    iv_mmf_name = 'UiPathRobot'
    iv_text     = 'Hello World from ABAP'
  ).

  lo_mmf->append(
    iv_mmf_name = 'UiPathRobot'
    iv_text     = ', so long, and thanks for all the fish'
  ).

  SELECT CARRID, CONNID, FLDATE, PRICE, CURRENCY, PLANETYPE, SEATSMAX,
    SEATSOCC, PAYMENTSUM, SEATSMAX_B, SEATSOCC_B, SEATSMAX_F, SEATSOCC_F
    FROM sflight INTO CORRESPONDING FIELDS OF TABLE @lt_flight.
  lv_text = /ui2/cl_json=>serialize( lt_flight ).
  lo_mmf->write(
    iv_mmf_name = 'UiPathRobot'
    iv_text     = lv_text
  ).

"-End-------------------------------------------------------------------

We set two breakpoints and execute the PowerShell script.

SAP ABAP Study Materials, SAP ABAP Tutorial and Material, SAP ABAP Guides

If the PowerShell script stops at the first breakpoint we start the ABAP report with a breakpoint at the select command.

SAP ABAP Study Materials, SAP ABAP Tutorial and Material, SAP ABAP Guides

Now we execute the PowerShell script til the next breakpoint, execute the ABAP report and also the PowerShell script. Here the expected result.

SAP ABAP Study Materials, SAP ABAP Tutorial and Material, SAP ABAP Guides

PowerShell gets via MMF the data from the ABAP report and the ABAP report gets the data from the PowerShell script.

SAP ABAP Study Materials, SAP ABAP Tutorial and Material, SAP ABAP Guides

Excellent, now the last step to transpose this scenario into eCATT. We create a script an set a few parameters. In this example I reduce the script to get and set the data.

SAP ABAP Study Materials, SAP ABAP Tutorial and Material, SAP ABAP Guides

The protocol of the eCATT script shows exactly what we expected. The input data from the PowerShell script and the output data for the PowerShell script. The result in PowerShell is exactly the same as in the image above.

SAP ABAP Study Materials, SAP ABAP Tutorial and Material, SAP ABAP Guides

Great, all works as expected. This example shows us how to use PowerShell functions seamlessly in ABAP. Also it shows us how to use MMF for interprocess communications between different processes on the frontend server in combination with the backend server. This process could be a PowerShell script, like in this example, also it could be an RPA process like the UiPath robot. And we can transfer data in larger amount.

No comments:

Post a Comment