Tag: RDML

Length of string

In RDMLX, easy, just

#LEN := #STR.CurChars

But in RDML it’s not so easy and for some reason there’s not even a buit-in function to return the length of a string.

So I made this:

Define Field(#W_C256) Type(*CHAR) Length(256)
Define Field(#W_C256_B) Type(*CHAR) Length(256)
Define Field(#W_C1) Type(*CHAR) Length(1)
Define Field(#W_D3_0) Type(*DEC) Length(3) Decimals(0)

Subroutine Name(LENGTH) Parms((#W_C256 *RECEIVED) (#W_D3_0 *RETURNED))
  Use Builtin(RIGHT) With_Args(#W_C256) To_Get(#W_C256_B)
  Substring Field(#W_C256_B 256 1) Into_Field(#W_C1)
  Use Builtin(REVERSE) With_Args(#W_C256) To_Get(#W_C256_B)
  Use Builtin(SCANSTRING) With_Args(#W_C256_B #W_C1) To_Get(#W_D3_0)
  Change Field(#W_D3_0) To('(256 + 1) - #W_D3_0')
Endroutine

which I then include when necessary.

ROUND_UP – no, I don’t think so

The documentation for CHANGE’s ROUND_UP parameter states, among other things, that

If the intermediate work fields involved do not have at least one more decimal position than the field nominated in the FIELD parameter, then the ROUND_UP parameter will be ignored and treated as if ROUND_UP(*NO) was specified.

I have difficulty finding documentation on how intermediate work fields are created, if no PRECISION parameter is given, but there are a few implied hints in the documentation for PRECISION.

So, in my concrete example, I have one 9,0 number divided by a 10,0 number. The documentation implies the intermediate work fields will be 10,0. My result field is a 3,0 number.

The actual numbers are 122,682,586 and 1,000,000, respectively. I really would like to get 123 as the result in this particular case.

Just doing a

Change Field(#RESULT) To('#BIG / #SMALL')

gives me 122. Doing

Change Field(#RESULT) To('#BIG / #SMALL') Round_Up(*YES)

gives me 123. Great, but not something that ought to have worked. Doing the right thing,

Change Field(#RESULT) To('#BIG / #SMALL') Precision(11 1) Round_Up(*YES)

also gives me 123. And, though the parameter is called ROUND_UP, it actually does the “half adjust” which I found a description of that seems like it would do the right thing.


Note: Embarrassingly, I was under the impression that the length of a numeric field was the number of digits before the decimal. It was just recently I found out it was the length including the decimals.

Irritating warnings during check-in

The following RDML code generates 4 warnings:

Delete From_File(KMXDAT) Where('(#SASNR *EQ #WSASNR) *AND (#OWNER *EQ #WOWNER) *AND (#CHAIN *EQ #WCHAIN)')
From Host FFC: Func KMOX / Cmd DELETE / Seq 1082 ....
** Field SASNR was appended, or needs appending, to list of fields because of use in WHERE parameter
** Field OWNER was appended, or needs appending, to list of fields because of use in WHERE parameter
** Field CHAIN was appended, or needs appending, to list of fields because of use in WHERE parameter

However, if I have a logical view with those 3 fields as keys and use them, I get no warnings:

Delete From_File(KMXDAT) With_Key(#WSASNR #WOWNER #WCHAIN)

I understand why I get those warnings but as it is not only illogical but also impossible to add those 3 fields to a delete, they are irritating.

No Delete without a Where clause

Delete from file without a where clause causes the RDML function to fail.

This does not work:

Delete From_File(DSTA149)

This does:

Delete From_File(DSTA149) Where('''A'' = ''A''')

No error in compile or check-in.

EDIT:

My misunderstanding. Delete from file without a where clause deletes the previously fetched record. My RDML function failed as I had not fetched anything and I wanted to delete every record.

So

Fetch Fields(#OPDKOD #STATUS) From_File(DSTA149) With_Key(#SEASON #OWNER)
Delete From_File(DSTA149)

would delete one record, the one just fetched.

 

There is a difference between the RDML and RDMLX BIFs

There are some gems on the Lansa forums every once in a while. If you use Lansa (and if not, what are you doing here?) you should register there and follow the threads.

The thread about JSM/JSMX and builtin function signatures is very enlightening and I am quoting it here:

There is a difference between the RDML and RDMLX BIFs.

The RDML BIF cannot access the function internals to determine the working list field definition, so the SERVICE_LIST keyword is required on the command.

The RDMLX BIF can access the working list field definition, so the SERVICE_LIST keyword is not required and ignored.

RDML

1. The following command will only send the command string to the service.

use builtin(JSM_COMMAND) with_args(‘TEST KEY(VALUE)’) to_get(#JSMSTS #JSMMSG)

2. The following command will send the command string and all fields in the function to the service.

use builtin(JSM_COMMAND) with_args(‘TEST KEY(VALUE) SERVICE_EXCHANGE(*FIELD)’) to_get(#JSMSTS #JSMMSG)

3. The following command will send the command string, the working list and all fields in the function to the service.

use builtin(JSM_COMMAND) with_args(‘TEST KEY(VALUE) SERVICE_LIST(*FIELD)’) to_get(#JSMSTS #JSMMSG #WRKLST)

If a working list and the associated SERVICE_LIST keyword is used, then all fields are passed and a SERVICE_EXCHANGE(*FIELD) is not required on the same command.

RDMLX

1. The following command will only send the command string to the service.

use builtin(JSMX_COMMAND) with_args(#JSMXHDLE ‘TEST KEY(VALUE)’) to_get(#JSMXSTS #JSMXMSG)

2. The following command will send the command string and all fields in the function to the service.

use builtin(JSMX_COMMAND) with_args(#JSMXHDLE ‘TEST KEY(VALUE) SERVICE_EXCHANGE(*FIELD|*FIELDS)’) to_get(#JSMXSTS #JSMXMSG)

3. The following command will send the command string and working list to the service.

Note: the function fields are not sent.

use builtin(JSMX_COMMAND) with_args(#JSMXHDLE ‘TEST KEY(VALUE)’) to_get(#JSMXSTS #JSMXMSG #WRKLST)

4. The following command will send the command string, all fields in the function and working list to the service.

use builtin(JSMX_COMMAND) with_args(#JSMXHDLE ‘TEST KEY(VALUE) SERVICE_EXCHANGE(*FIELD|*FIELDS)’) to_get(#JSMXSTS #JSMXMSG #WRKLST)

5. The following command will send the command string and the function fields defined in the field list to the service.

use builtin(JSMX_COMMAND) with_args(#JSMXHDLE ‘TEST KEY(VALUE)’ #FLDLST) to_get(#JSMXSTS #JSMXMSG)

6. The following command will send the command string and function fields defined in the field list and the working list to the service.

use builtin(JSMX_COMMAND) with_args(#JSMXHDLE ‘TEST KEY(VALUE)’ #FLDLST) to_get(#JSMXSTS #JSMXMSG #WRKLST)

(slightly edited by me)

In summary:

  • In JSMX BIFs you can send a working list to the service, with or without sending all or some of the fields in the function.
  • In JSM BIFs you can only choose to send none or all of the fields in the function to the service, and if you send all fields you can also send a working list.

SOAP client example in RDML

There is a nice sample in the LANSA Integrator Guide but as with many of the examples the information about how to understand how something else could be done is more difficult to find.

So I spent some time trying to create a simple SOAP client. But, unlike the example in the guide, this one would be without fragments – a single root element with a number of elements directly within. Specifically, the GetCityWeatherByZIP public sample, as I was just trying the SOAP services out.

I followed the instructions in the guide to create an Integrator solution, map the parameters, build and deploy, but coming up to the actual program I had trouble getting the data from the SOAP response. The field were not being bound to the response.

It turned out to quite simple

My experiment is written in RDML as I thought it would be best to get in running on the least-capable platform first. In RDMLX you can have more than one connection at the same time, using different JSMX handles, but I did not need that here.

Function Options(*DIRECT)
* The following fields should be defined in the dictionary
Define Field(#JSMSTS) Type(*CHAR) Length(20)
Define Field(#JSMMSG) Type(*CHAR) Length(256)
Define Field(#JSMCMD) Type(*CHAR) Length(256)
* The following fields are used by the soap binding map
Define Field(#RESULT) Type(*CHAR) Length(256) Label('Reply:')
Define Field(#DEBUG) Reffld(#RESULT) Label('Debug:')
* The following fields are used by the soap binding map
Define Field(#ZIP) Type(*CHAR) Length(5) Label('Zip..:')
Define Field(#CITY) Type(*CHAR) Length(20)
Define Field(#DESC) Type(*CHAR) Length(20)
Define Field(#PRES) Type(*CHAR) Length(20)
Define Field(#RELHUM) Type(*CHAR) Length(20)
Define Field(#REMARKS) Type(*CHAR) Length(20)
Define Field(#RESPONSE) Type(*CHAR) Length(20)
Define Field(#STATE) Type(*CHAR) Length(20)
Define Field(#SUCCESS) Type(*CHAR) Length(20)
Define Field(#TEMP) Type(*CHAR) Length(20)
Define Field(#VISI) Type(*CHAR) Length(20)
Define Field(#WIND) Type(*CHAR) Length(20)
Define Field(#WINDCHILL) Type(*CHAR) Length(20)
Def_List Name(#SOAPLIST) Fields(#CITY #DESC #PRES #RELHUM #REMARKS #RESPONSE #STATE #SUCCESS #TEMP #VISI #WIND #WINDCHILL) Type(*WORKING)
Change Field(#ZIP) To('''80210''') /* Denver */
Dowhile Cond('A *EQ A')
Request Fields((#FUNCTION *L1 *P2 *OUT) (#DATE *L1 *P73 *OUT) (#ZIP *L4 *P2 *LAB) (#DEBUG *L7 *P2 *LAB *OUT)) Design(*ACROSS) Identify(*NOID) Down_Sep(1) Across_Sep(1) Exit_Key(*YES *NEXT) Menu_Key(*NO) Text((*TMAP002 1 1)) Std_Head(*NO)
* F-keys
Case Of_Field(#IO$KEY)
When Value_Is('*EQ ''03''')
Return
Endcase
Change Field(#DEBUG) To(*BLANKS)
* Open service
Use Builtin(JSM_OPEN) To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Load service
Change Field(#JSMCMD) To('SERVICE_LOAD SERVICE(SOAPAgentService) TRACE(*YES)')
Use Builtin(JSM_COMMAND) With_Args(#JSMCMD) To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Open SOAP service
Change Field(#JSMCMD) To('OPEN SERVICE(WEATHER2)')
Use Builtin(JSM_COMMAND) With_Args(#JSMCMD) To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Set operation - GETCITYWEATHERBYZIP
Change Field(#JSMCMD) To('SET OPERATION(GETCITYWEATHERBYZIP)')
Use Builtin(JSM_COMMAND) With_Args(#JSMCMD) To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Set parameter - ZIP
Change Field(#JSMCMD) To('SET PARAMETER(ZIP) SERVICE_EXCHANGE(*FIELD)')
Use Builtin(JSM_COMMAND) With_Args(#JSMCMD) To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Call remote service
Change Field(#JSMCMD) To('CALL SERVICE_LIST(CITY,DESC,PRES,RELHUM,REMARKS,RESPONSE,STATE,SUCCESS,TEMP,VISI,WIND,WINDCHILL)')
Use Builtin(JSM_COMMAND) With_Args(#JSMCMD) To_Get(#JSMSTS #JSMMSG #SOAPLIST)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
Change Field(#JSMCMD) To('IS NOT_NULL(*RETURN)')
Use Builtin(JSM_COMMAND) With_Args(#JSMCMD) To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Close SOAP service
Use Builtin(JSM_COMMAND) With_Args('CLOSE') To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Unload service
Use Builtin(JSM_COMMAND) With_Args('SERVICE_UNLOAD') To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
* Close service
Use Builtin(JSM_CLOSE) To_Get(#JSMSTS #JSMMSG)
Execute Subroutine(CHECK) With_Parms(#JSMSTS #JSMMSG)
Display Fields(#CITY #DESC #PRES #RELHUM #REMARKS #RESPONSE #STATE #SUCCESS #TEMP #VISI #WIND #WINDCHILL)
Endwhile
* Check routine
Subroutine Name(CHECK) Parms((#JSMSTS *RECEIVED) (#JSMMSG *RECEIVED))
If Cond('#JSMSTS *NE OK')
Use Builtin(JSM_CLOSE) To_Get(#JSMSTS #JSMMSG)
Use Builtin(BCONCAT) With_Args(#DEBUG 'error') To_Get(#DEBUG)
Endif
Endroutine

The tiny change that made this work was changing

Change Field(#JSMCMD) To('CALL')

to

Change Field(#JSMCMD) To('CALL SERVICE_LIST(CITY,DESC,PRES,RELHUM,REMARKS,RESPONSE,STATE,SUCCESS,TEMP,VISI,WIND,WINDCHILL)')

which is the list of all the fields I had defined in the Integrator setup. It did not work when I was testing it with just a couple of the fields.

If the response is best split in fragments, I expect the code in the guide to be usable.

Other resources:

http://www.lansa.com/resources/implementing-soap-web-services-with-lansa-integrator.htm

http://www.lansa.com/support/tips/soaptutorials.htm

http://www.lansa.com/support/tips/t0382.htm