Inconsistency with the iData class.
As seen in the documentation for iData : http://yips.idevcloud.com/wiki/index.php/XMLSERVICE/Python#iData
The arguments at the top are listed as ikey, iparm, itype, ival, and iopt:
ikey (str): XML <data ... var="ikey"> for parsing output.
iparm (obj): dom for parameter or return or ds.
itype (obj): data type [see XMLSERVICE types, '3i0', ...].
ival (obj): data type value.
iopt (dict): option - dictionay of options (below)
However the source shows that the the class doesn't use iparm in the init function:
def __init__(self, ikey, itype, ival, iopt={}):
We're having some issues with getting data back from srvpgms and we think this information might help.
Thanks
Comments (39)
-
Account Deleted -
Account Deleted Close to the weekend. So here is an example srvpgm call.
# D ARRAYMAX c const(999) # D dcRec_t ds qualified based(Template) # D dcMyName 10A # D dcMyJob 4096A # D dcMyRank 10i 0 # D dcMyPay 12p 2 # *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # * zzarray: check return array aggregate # *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # P zzarray B export # D zzarray PI likeds(dcRec_t) dim(ARRAYMAX) # D myName 10A # D myMax 10i 0 # D myCount 10i 0 itool = iToolKit() itool.add(iCmd('chglibl', 'CHGLIBL LIBL(XMLSERVICE)')) itool.add( iSrvPgm('zzarray','ZZSRV','ZZARRAY') .addParm(iData('myName','10a','ranger')) .addParm(iData('myMax','10i0','8')) .addParm(iData('myCount','10i0','',{'enddo':'mycnt'})) .addRet( iDS('dcRec_t',{'dim':'999','dou':'mycnt'}) .addData(iData('dcMyName','10a','')) .addData(iData('dcMyJob','4096a','')) .addData(iData('dcMyRank','10i0','')) .addData(iData('dcMyPay','12p2','')) ) )
-
Account Deleted Also ... thank you for finding the documentation error. The iparm did not belong.
class iData (iBase) Data value child node for iPgm, iSrvPgm, or iDS data structures. Args: ikey (str): XML <data ... var="ikey"> for parsing output. itype (obj): data type [see XMLSERVICE types, '3i0', ...]. ival (obj): data type value. iopt (dict): option - dictionay of options (below) {'dim':'n'} : optional - XMLSERVICE dimension/occurs number. {'varying':'on|off|2|4'} : optional - XMLSERVICE varying {'varying':'off'}. {'hex':'on|off'} : optional - XMLSERVICE hex chracter data {'hex':'off'}. {'enddo':'label'} : optional - XMLSERVICE enddo until label. {'setlen':'label'} : optional - XMLSERVICE set calc length label. {'offset':'n'} : optional - XMLSERVICE offset label. {'next':'label'} : optional - XMLSERVICE next offset label (value).
-
Thanks for the clarification on the iParm. As for the code we are trying to run, I have included the relevant sources below.
Script we are testing with:
from itoolkit import * from itoolkit.lib.ilibcall import * itool = iToolKit() itransport = iLibCall() itool.add(iSrvPgm('unimportant_name','HFASVPR','GetProductHierarchy',{"error":"on"}) .addParm(iData('prFormat','10a','')) .addParm(iData('prType','1a','L')) .addParm(iData('prValue','15a','UP')) .addParm(iData('prChannel','5a','')) .addParm(iData('prChannelDesc','50a','')) .addParm(iData('prMPG','5a','')) .addParm(iData('prMPGDesc','50a','')) .addParm(iData('prClass','5a','')) .addParm(iData('prClassDesc','50a','')) .addParm(iData('prLine','5a','')) .addParm(iData('prLineDesc','50a','')) .addParm(iData('prPart','15a','')) .addParm(iData('prPartDesc','30a','')) .addRet(iData('ret_val','1a','')) ) itool.call(itransport) unimportant_name = itool.dict_out('unimportant_name') print(unimportant_name)
Response from script:
{ 'prMPGDesc': '', 'prClassDesc': '', 'prMPG': '', 'prPart': '', 'prLineDesc': '', 'success': '+++ success HFASVPR GetProductHierarchy', 'prChannel': '', 'prPartDesc': '', 'ret_val': '0', 'prFormat': '', 'prType': 'M', 'prValue': 'LVA', 'prChannelDesc': '', 'prClass': '', 'prLine': '' }
As you can see, it isn't correctly populating any of the parameters except the ones I send in.
Copyfile for service program:
/02 ************************************************************************* /02 * GetProductHierarchy - will read up through the nf product /02 * hierarchy to return the values and descriptions. /02 * If a part a type of P is passed in for part will get the /02 * hierarchy from PM. /02 * All values below in the hierarchy will be blank. /02 * /02 * prType(incoming) = product hierarchy type /02 * /02 * prValue(incoming) = the channel, mpg, class or /02 * line value. /02 * H = Channel /02 * M = MPG /02 * C = Class /02 * L = Line /02 * P = Part /02 * prChannel(outgoing) = Product Channel /02 * prChannelDesc = Channel Description /02 * prMPG = Product MPG /02 * prMPGDesc = MPG Description /02 * prClass = Product Class /02 * prClassDesc = Class Description /02 * prLine = Product Line /02 * prLineDesc = Line Description /02 * /02 * ex. Incoming Values /02 * prType = 'L' /02 * prValue= 'UP' /02 * /02 * Outgoing Values /02 * prChannel = 'B' /02 * prChannelDesc = 'Utility' /02 * prMPG = 'LVA' /02 * prMPGDesc = 'LOW VOLTAGE ALUMINUM' /02 * prClass = '71' /02 * prClassDesc = UNDERGROUND SERVICE /02 * prLine = UP /02 * prLineDesc = US-MULTI-CONDUCTOR ABUSE RESISTANT /02 * /02 ************************************************************************* /02 DGetProductHierarchy... /02 D Pr 1n ExtProc('GetProductHierarchy') /02 D prFormat 10a Value /02 D prType 1a Value /02 D prValue 15a Value /02 D prChannel 5a /02 D prChannelDesc 50a /02 D prMPG 5a /02 D prMPGDesc 50a /02 D prClass 5a /02 D prClassDesc 50a /02 D prLine 5a /02 D prLineDesc 50a /02 D prPart 15a /02 D prPartDesc 30a /03 /03 *************************************************************************
Source of relevant program:
/01 //***************************************************** /01 //* Retrieve the Product Hierarchy * /01 //***************************************************** /01 /01 PGetProductHierarchy... /01 P B Export /01 DGetProductHierarchy... /01 D Pi 1n /01 D p0Format 10a Value /01 D p0Type 1a Value /01 D p0Value 15a Value /01 D p0Channel 5a /01 D p0ChannelDesc 50a /01 D p0MPG 5a /01 d p0MPGDesc 50a /01 d p0Class 5a /01 d p0ClassDesc 50a /01 d p0Line 5a /01 d p0LineDesc 50a /01 d p0Part 15a /01 d p0PartDesc 30a /01 /01 d nfpds e ds extname(nfp) Qualified /01 d pmpds e ds extname(pmp) Qualified /01 /01 d format s 10a Inz /01 d NfType s 2a Inz /01 d NfValue s 5a Inz /01 d Part s 15a Inz /01 d NoteInformationV00... /01 d ds LikeDs(ObjNoteFileDsV00) /01 d ProductInformationDSV00... /01 d ds LikeDs(ObjProductMasterDSV00) /01 d ReturnFlag s 1n Inz(*on) /01 d HierarchyFlag s 1n Inz(*off) /01 /01 /Free /01 /01 If p0format <> *blanks /01 and p0format = 'V00'; /01 Return ReturnFlag = *off; /01 EndIf; /01 /01 // If the part is passed in get the line level from /01 // PMP and then go to the note file to get the hierarchy starting at /01 // the line level /01 If p0Type = 'P'; //Part /01 Part = p0Value; /01 clear pmpds; /01 clear format; /01 getProductMaster(format:Part: /01 %len(ProductInformationDSV00): /01 ProductInformationDSV00); /01 eval-corr pmpds = ProductInformationDSV00; /01 If Part = pmpds.pmpart; /01 p0Part = pmpds.pmpart; /01 p0PartDesc = pmpds.pmdesc; /01 p0Value = pmpds.pmplin; /01 Else; /01 HierarchyFlag = *on; /01 ReturnFlag = *off; /01 EndIf; /01 EndIf; /01 /01 // Set the note file type /01 Select; /01 When p0Type = 'P'; /01 NfType = 'PL'; /01 When p0Type = 'L'; /01 NfType = 'PL'; /01 When p0Type = 'C'; /01 NfType = 'PR'; /01 When p0Type = 'M'; /01 NfType = 'MG'; /01 When p0Type = 'H'; /01 NfType = 'MR'; /01 EndSl; /01 NfValue = p0Value; /01 /01 // Loop through the note files to get the hierarchy /01 Dow HierarchyFlag = *off; /01 clear nfpds; /01 clear format; /01 getnotefile(format:NfType:NfValue:00: /01 %len(NoteInformationV00): /01 NoteInformationV00); /01 eval-corr nfpds = NoteInformationV00; /01 /01 // Check if the note type is found. If not turn off the result flag and leave the loop /01 If NfType = *blanks /01 or NfType <> nfpds.nftyp; /01 ReturnFlag = *off; /01 Leave; /01 EndIf; /01 /01 // Populate the return values and set the values for the next hierarchy level. /01 If nfType = 'PL'; /01 p0Line = nfpds.nfnumb; /01 p0LineDesc = %subst(nfpds.nfdesc:1:48); /01 NfType = 'PR'; /01 NfValue = %subst(nfpds.nfdesc:49:2); /01 ElseIf nfType = 'PR'; /01 p0Class = nfpds.nfnumb; /01 p0ClassDesc = %subst(nfpds.nfdesc:1:45); /01 NfType = 'MG'; /01 NfValue = %subst(nfpds.nfdesc:46:5); /01 ElseIf nfType = 'MG'; /01 p0MPG = nfpds.nfnumb; /01 p0MPGDesc = %subst(nfpds.nfdesc:1:39); /01 NfType = 'MR'; /01 NfValue = %subst(nfpds.nfdesc:40:1); /01 ElseIf nfType = 'MR'; /01 p0Channel = nfpds.nfnumb; /01 p0ChannelDesc = %subst(nfpds.nfdesc:1:39); /01 HierarchyFlag = *on; /01 EndIf; /01 EndDo; /01 /01 Return ReturnFlag; /01 /01 /End-Free /01 /01 PGetProductHierarchy... /01 P E
Let me know if there is anything else you need. I realize this may fall outside the scope of the project, so if you cannot help, I understand.
Thanks in advance!
-
Account Deleted Yes. Expected failure. XMLSERVICE does not support pass by value.
/01 DGetProductHierarchy... /01 D Pi 1n /01 D p0Format 10a Value ... unsupported /01 D p0Type 1a Value ... unsupported /01 D p0Value 15a Value ... unsupported /01 D p0Channel 5a ... ok, pass by reference
-
Account Deleted You will need the next version of xmlservice 2 for pass by value support. Name is xjservice, as it supports both json and xml.
-
Ah, that you for that. I missed that part of the documentation. That would explain it. Do we have an ETA for xjservice? Just a general idea of year or quarter so we can make a better decision about moving forward from here. Thanks.
-
Account Deleted Well ... i have two answers ...
1) xjservice (new) -- targeting around common ... bit aggressive ... may slip into summer. This technology is completely different than xmlservice. Aka, in fact, so far no PASE utilization at all ... to speed things up.
2) xmlservice (old) -- I did put a place holder in for pass-by value. Did not really test the idea as very few 'modernized' *SRVPGMs where using 'value' keyword. Blah Blah ... we should all be using 100% free form with dcl-s, dcl-ds, etc. (i am in xjservice).
Proposal. If you are interested in being a 'test subject' (think hamster wheel) ... it is possible to implement by value in the old technology. Do you want to work together for a week or so and get it working???
-
Account Deleted Well ... Jimmy Cricket ... this simple test worked (by value) with no modifications to xmlservice 1.9.9.1. The luck of the Irish, but i am Norwegian.
H AlwNull(*UsrCtl) /copy test_h // **************************************************** // xmlservice/xmlstoredp SRVPGM // **************************************************** dcl-pr runClient ind; pIPCSP char(1024); pCtl char(1024); pIClob pointer; szIClob int(10); pOClob pointer; szOClob int(10); end-pr; // **************************************************** // TESTPGM // **************************************************** dcl-pr Main extpgm; end-pr; // **************************************************** // main(): Control flow // **************************************************** dcl-pi Main; end-pi; // **************************************************** // tests // **************************************************** dcl-s i int(10) inz(0); testboom(); testval1(); testval2(); testval4(); testval5(); testval6(); return; // **************************************************** // interface to old xmlservice // **************************************************** dcl-proc xjRun; dcl-pi *N; xjIn pointer value; xjOut pointer value; xjInSz int(10) value; xjOutSz int(10) value; end-pi; dcl-s ipc char(1024) inz(*BLANKS); dcl-s ctl char(1024) inz(*BLANKS); ipc = '*na'; ctl = '*here'; runClient(ipc:ctl:xjIn:xjInSz:xjOut:xjOutSz); end-proc; // **************************************************** // test: TESTZSRV.TESTBOOM // **************************************************** dcl-proc testboom; dcl-pi *N; end-pi; dcl-s xjInData char(4096) inz(*BLANKS); dcl-s xjOutData char(4096) inz(*BLANKS); dcl-s xjIn pointer inz(%addr(xjInData)); dcl-s xjOut pointer inz(%addr(xjOutData)); xjInData = '<?xml version="1.0"?>' + '<myscript>' + '<pgm name="TESTZSRV" lib="'+TEST_LIB+'" func="TESTBOOM">' + ' <return>' + ' <data type="10i0">3</data>' + ' </return>' + '</pgm>' + '</myscript>' + x'00'; xjRun(xjIn:xjOut:4096:4096); end-proc; // **************************************************** // test: TESTZSRV.TESTVAL1 // **************************************************** dcl-proc testval1; dcl-pi *N; end-pi; dcl-s xjInData char(4096) inz(*BLANKS); dcl-s xjOutData char(4096) inz(*BLANKS); dcl-s xjIn pointer inz(%addr(xjInData)); dcl-s xjOut pointer inz(%addr(xjOutData)); xjInData = '<?xml version="1.0"?>' + '<myscript>' + '<pgm name="TESTZSRV" lib="'+TEST_LIB+'" func="TESTVAL1">' + ' <parm io="both" by="val">' + ' <data type="10i0">3</data>' + ' </parm>' + ' <return>' + ' <data type="10i0">3</data>' + ' </return>' + '</pgm>' + '</myscript>' + x'00'; xjRun(xjIn:xjOut:4096:4096); end-proc; // **************************************************** // test: TESTZSRV.TESTVAL2 // **************************************************** dcl-proc testval2; dcl-pi *N; end-pi; dcl-s xjInData char(4096) inz(*BLANKS); dcl-s xjOutData char(4096) inz(*BLANKS); dcl-s xjIn pointer inz(%addr(xjInData)); dcl-s xjOut pointer inz(%addr(xjOutData)); xjInData = '<?xml version="1.0"?>' + '<myscript>' + '<pgm name="TESTZSRV" lib="'+TEST_LIB+'" func="TESTVAL2">' + ' <parm io="both" by="val">' + ' <data type="10i0">3</data>' + ' </parm>' + ' <parm io="both" by="val">' + ' <data type="20i0">4</data>' + ' </parm>' + ' <return>' + ' <data type="20i0">3</data>' + ' </return>' + '</pgm>' + '</myscript>' + x'00'; xjRun(xjIn:xjOut:4096:4096); end-proc; // **************************************************** // test: TESTZSRV.TESTVAL4 // **************************************************** dcl-proc testval4; dcl-pi *N; end-pi; dcl-s xjInData char(4096) inz(*BLANKS); dcl-s xjOutData char(4096) inz(*BLANKS); dcl-s xjIn pointer inz(%addr(xjInData)); dcl-s xjOut pointer inz(%addr(xjOutData)); xjInData = '<?xml version="1.0"?>' + '<myscript>' + '<pgm name="TESTZSRV" lib="'+TEST_LIB+'" func="TESTVAL4">' + ' <parm io="both" by="val">' + ' <data type="3i0">4</data>' + ' </parm>' + ' <parm io="both" by="val">' + ' <data type="5i0">5</data>' + ' </parm>' + ' <parm io="both" by="val">' + ' <data type="10i0">6</data>' + ' </parm>' + ' <parm io="both" by="val">' + ' <data type="20i0">7</data>' + ' </parm>' + ' <return>' + ' <data type="16a">olive</data>' + ' </return>' + '</pgm>' + '</myscript>' + x'00'; xjRun(xjIn:xjOut:4096:4096); end-proc; // **************************************************** // test: TESTZSRV.TESTVAL5 // **************************************************** dcl-proc testval5; dcl-pi *N; end-pi; dcl-s xjInData char(4096) inz(*BLANKS); dcl-s xjOutData char(4096) inz(*BLANKS); dcl-s xjIn pointer inz(%addr(xjInData)); dcl-s xjOut pointer inz(%addr(xjOutData)); xjInData = '<?xml version="1.0"?>' + '<myscript>' + '<pgm name="TESTZSRV" lib="'+TEST_LIB+'" func="TESTVAL5">' + ' <parm io="both">' + ' <data type="3i0">4</data>' + ' </parm>' + ' <parm io="both">' + ' <data type="5i0">5</data>' + ' </parm>' + ' <parm io="both">' + ' <data type="10i0">6</data>' + ' </parm>' + ' <parm io="both">' + ' <data type="20i0">7</data>' + ' </parm>' + ' <return>' + ' <data type="20i0">3</data>' + ' </return>' + '</pgm>' + '</myscript>' + x'00'; xjRun(xjIn:xjOut:4096:4096); end-proc; // **************************************************** // test: TESTZSRV.TESTVAL6 // **************************************************** dcl-proc testval6; dcl-pi *N; end-pi; dcl-s xjInData char(4096) inz(*BLANKS); dcl-s xjOutData char(4096) inz(*BLANKS); dcl-s xjIn pointer inz(%addr(xjInData)); dcl-s xjOut pointer inz(%addr(xjOutData)); xjInData = '<?xml version="1.0"?>' + '<myscript>' + '<pgm name="TESTZSRV" lib="'+TEST_LIB+'" func="TESTVAL6">' + ' <return>' + ' <data type="16a">frog</data>' + ' </return>' + '</pgm>' + '</myscript>' + x'00'; xjRun(xjIn:xjOut:4096:4096); end-proc;
-
Nice! Just to be clear (sorry, I'm a Python dev, not an RPG dev), all that needed to be done was tack on a
by="val"
to the XML and it works? I'm going to be calling these programs from Python so in order to make this work I would need to 1) Build the XML manually to include theby="val"
2) Call into the XMLSERVICE directly with the XML created (therefore skipping the itoolkit entirely)Is that correct? Or is there a way I can use this in addition to the itoolkit, so I can use the nice features like dict_out?
-
Account Deleted Ok ... wonders ... looks like it worked from python as well ...
(incorrect python syntax removed -- see last entry)
-
Account Deleted (incorrect python syntax removed -- see last entry)
-
My attempt wasn't quite as successful:
import os from itoolkit import * from itoolkit.lib.ilibcall import * itool = iToolKit() itransport = iLibCall() itool.add(iCmd('chglibl', 'CHGLIBL LIBL(XMLSERVICE)')) itool.add(iSrvPgm('unimportant_name','HFASVPR','GetProductHierarchy',{"error":"on"}) .addParm(iData('prFormat','10a','',{'by':'val'})) .addParm(iData('prType','1a','M',{'by':'val'})) .addParm(iData('prValue','15a','LVA',{'by':'val'})) .addParm(iData('prChannel','5a','')) .addParm(iData('prChannelDesc','50a','')) .addParm(iData('prMPG','5a','')) .addParm(iData('prMPGDesc','50a','')) .addParm(iData('prClass','5a','')) .addParm(iData('prClassDesc','50a','')) .addParm(iData('prLine','5a','')) .addParm(iData('prLineDesc','50a','')) .addParm(iData('prPart','15a','')) .addParm(iData('prPartDesc','30a','')) .addRet(iData('ret_val','1a','')) ) itool.call(itransport) chglibl = itool.dict_out('chglibl') unimportant_name = itool.dict_out('unimportant_name') print(chglibl) print(unimportant_name['success']) if 'success' in unimportant_name: print(unimportant_name)
Output:
$ > python3 srvpgm_test.py {'jobuser': 'HQPGMXT', 'dftccsid': '37', 'error1': '202', 'jobipcskey': 'FFFFFFFF', 'curuser': 'HQPGMXT', 'jobnbr': '632888', 'pa seccsid': '819', 'jobname': 'QP0ZSPWP', 'ccsid': '65535', 'error': '*** error CHGLIBL LIBL(XMLSERVICE)', 'jobipc': '*na', 'xmlhin t': 'CHGLIBL LIBL(XMLSERVICE)', 'version': 'XML Toolkit 1.9.2', 'xmlhint2': 'CHGLIBL LIBL(XMLSERVICE)', 'syslibl': 'ALTQSYS DMSDA TA QSYS QSYS2 QHLPSYS QUSRSYS RBTSYSLIB', 'usrlibl': 'QTEMP HQPGMXT QGPL HFADTAMC HFASYSMAN HFASYS HFASECURE UTLFIL UTLPRD HFACGI MC HFACGIGB HFACGIGC HFAOBJMC HFAOBJGB HFAOBJGC HFASRCMC HFASRCGB HFASRCGC HFAOBJ HFASRCGIMC HFASRCGIGB HFASRCGIGC ACMSCTL AVPFIL GLDBFMC G3X4DTA EASYMENU'} +++ success HFASVPR GetProductHierarchy {'prChannel': '', 'prClass': '', 'prType': 'M', 'prLine': '', 'prChannelDesc': '', 'prClassDesc': '', 'prPartDesc': '', 'prPart': '', 'prFormat': '', 'prLineDesc': '', 'prValue': 'LVA', 'success': '+++ success HFASVPR GetProductHierarchy', 'prMPGDesc': '', 'prMPG': '', 'ret_val': '0'} $
-
Account Deleted Nope ... mixing ref and val did not work ...
(incorrect python syntax removed -- see last entry)
-
Account Deleted Yes ... i will take a quick look ... after 'value(s)' probably misaligned pointers off quadword (guess)... we are actually closer than i thought.
-
Account Deleted Almost worked ... many pointer locations are good following values, but pointing wrong place. So, looks like we can fix this, but take a while to test it out. What would you like to do?
(Reminder ... half day invested so far ... could be working before Friday if you want to try)
-
I think we'd be available to test out some fixes, as waiting until beginning of summer for the new service isn't preferable. What do you need from me?
-
Account Deleted DO you have a few more parameter layouts that mix 'value' with 'ref' like GetProductHierarchy? If yes, please post a few (*). I will try to fix up value (if possible), and post a new version on yips for our experiment (i will add link).
(*) Reminder: This is a public forum, so do not post business confidential information (everyone can see).
-
Account Deleted Ok. This already works. We just did not use the correct python syntax. I forgot addParm(iData) was a convenience function for people. We would need to add a new method like addParmByValue(iData).
Bottom line: Possible 'value' already works (i must have fixed it, then forgot).
bash-4.3$ python testval2.py +++ success CHGLIBL LIBL(XMLSERVICE) ch1 Great SyFy 4mpg i dunno mpg low ye haw lin5 line man county part1 hairy frog
import os from itoolkit import * from itoolkit.lib.ilibcall import * itransport = iLibCall() itool = iToolKit() itool.add(iCmd('chglibl', 'CHGLIBL LIBL(XMLSERVICE)')) # dcl-proc GetSmart export; # dcl-pi *N; # p0Format char(10) Value; # p0Type char(1) Value; # p0Value char(15) Value; # p0Channel char(5); # p0ChannelDesc char(50); # p0MPG char(5); # p0MPGDesc char(50); # p0Class char(5); # p0ClassDesc char(50); # p0Line char(5); # p0LineDesc char(50); # p0Part char(15); # p0PartDesc char(30); # end-pi; s1 = iSrvPgm('getsmart','TESTZSRV','GETSMART') p1 = iParm('p1',{'by':'val'}) p1.add(iData('p0Format','10a','')) p2 = iParm('p2',{'by':'val'}) p2.add(iData('p0Type','1a','')) p3 = iParm('p3',{'by':'val'}) p3.add(iData('p0Value','15a','')) s1.add(p1) s1.add(p2) s1.add(p3) s1.addParm(iData('p0Channel','5a','')) s1.addParm(iData('p0ChannelDesc','50a','')) s1.addParm(iData('p0MPG','5a','')) s1.addParm(iData('p0MPGDesc','50a','')) s1.addParm(iData('p0Class','5a','')) s1.addParm(iData('p0ClassDesc','50a','')) s1.addParm(iData('p0Line','5a','')) s1.addParm(iData('p0LineDesc','50a','')) s1.addParm(iData('p0Part','15a','')) s1.addParm(iData('p0PartDesc','30a','')) itool.add(s1) itool.call(itransport) chglibl = itool.dict_out('chglibl') getsmart = itool.dict_out('getsmart') print(chglibl['success']) # print(getsmart) # print(itool.xml_in()) # print(itool.xml_out()) if 'success' in getsmart: print(getsmart['p0Format']) print(getsmart['p0Type']) print(getsmart['p0Value']) print(getsmart['p0Channel']) print(getsmart['p0ChannelDesc']) print(getsmart['p0MPG']) print(getsmart['p0MPGDesc']) print(getsmart['p0Class']) print(getsmart['p0ClassDesc']) print(getsmart['p0Line']) print(getsmart['p0LineDesc']) print(getsmart['p0Part']) print(getsmart['p0PartDesc'])
dcl-proc GetSmart export; dcl-pi *N; p0Format char(10) Value; p0Type char(1) Value; p0Value char(15) Value; p0Channel char(5); p0ChannelDesc char(50); p0MPG char(5); p0MPGDesc char(50); p0Class char(5); p0ClassDesc char(50); p0Line char(5); p0LineDesc char(50); p0Part char(15); p0PartDesc char(30); end-pi; p0Format = 'i be fmt'; p0Type = '9'; p0Value = 'i be value'; p0Channel = 'ch1'; p0ChannelDesc = 'Great SyFy'; p0MPG = '4mpg'; p0MPGDesc = 'i dunno mpg'; p0Class = 'low'; p0ClassDesc = 'ye haw'; p0Line = 'lin5'; p0LineDesc = 'line man county'; p0Part = 'part1'; p0PartDesc = 'hairy frog'; end-proc;
-
Account Deleted Short version, 'value' syntax looks like following.
# dcl-proc GetSmart export; # dcl-pi *N; # p0Format char(10) Value; # p0Type char(1) Value; # p0Value char(15) Value; # p0Channel char(5); # p0ChannelDesc char(50); # p0MPG char(5); # p0MPGDesc char(50); # p0Class char(5); # p0ClassDesc char(50); # p0Line char(5); # p0LineDesc char(50); # p0Part char(15); # p0PartDesc char(30); # end-pi; s1 = iSrvPgm('getsmart','TESTZSRV','GETSMART') p1 = iParm('p1',{'by':'val'}) p1.add(iData('p0Format','10a','')) p2 = iParm('p2',{'by':'val'}) p2.add(iData('p0Type','1a','')) p3 = iParm('p3',{'by':'val'}) p3.add(iData('p0Value','15a','')) s1.add(p1) s1.add(p2) s1.add(p3) s1.addParm(iData('p0Channel','5a','')) s1.addParm(iData('p0ChannelDesc','50a','')) s1.addParm(iData('p0MPG','5a','')) s1.addParm(iData('p0MPGDesc','50a','')) s1.addParm(iData('p0Class','5a','')) s1.addParm(iData('p0ClassDesc','50a','')) s1.addParm(iData('p0Line','5a','')) s1.addParm(iData('p0LineDesc','50a','')) s1.addParm(iData('p0Part','15a','')) s1.addParm(iData('p0PartDesc','30a',''))
BTW -- Most people demand idea of parameter 'container of data' to remain abstract (not an object iParm). This is of course faulty logic, because 'contained' iData changes meaning with context inside iDs, iRet and iParm (all containers). Aka, 'value' has no meaning in iDs or iRet. Only when iParm contains iData does the attribute 'value' or 'const' have meaning. Some folks get really exercised about this logic. Anyway, pgm.addParm(iData or iDs) allows a verb representation of an action that adds a parameter object to the PGM/SRVPGM containing iData or iDS (aggregate of iData). All in all, we may have to add another verb(ish) addParmByVal(iData or iDs) to keep the 'no parameter' pitch forks at bay.
-
The new version isn't working for me still:
from itoolkit import * from itoolkit.lib.ilibcall import * itool = iToolKit() itransport = iLibCall() s1 = iSrvPgm('prod_hier','HFASVPR','GetProductHierarchy') p1 = iParm('p1',{'by':'val'}) p1.add(iData('prFormat','10a','')) p2 = iParm('p2',{'by':'val'}) p2.add(iData('prType','1a','L')) p3 = iParm('p3',{'by':'val'}) p3.add(iData('prValue','15a','UP')) s1.add(p1) s1.add(p2) s1.add(p3) s1.addParm(iData('prChannel','5a','')) s1.addParm(iData('prChannelDesc','50a','')) s1.addParm(iData('prMPG','5a','')) s1.addParm(iData('prMPGDesc','50a','')) s1.addParm(iData('prClass','5a','')) s1.addParm(iData('prClassDesc','50a','')) s1.addParm(iData('prLine','5a','')) s1.addParm(iData('prLineDesc','50a','')) s1.addParm(iData('prPart','15a','')) s1.addParm(iData('prPartDesc','30a','')) itool.add(s1) itool.call(itransport) prod_hier = itool.dict_out('prod_hier') print(prod_hier)
Output:
$ > python3 srvpgm_test.py {'prLine': '', 'success': '+++ success HFASVPR GetProductHierarchy', 'prChannel': '', 'prClass': '', 'prChannelDesc': '', 'prMPG ': '', 'prClassDesc': '', 'prMPGDesc': '', 'prValue': 'UP', 'prType': 'L', 'prFormat': ' ', 'prPartDesc': '', 'prLineDesc': '', ' prPart': ''} $
Just to verify we are using the same version, these are my package versions:
$ > pip3 freeze bottle==0.12.8 flipflop==1.0 ibm-db==2.0.5.5 ibm-db-django==1.0.9.1 itoolkit==1.2 six==1.10.0 $
-
I am also still in the process of finding more service programs that use a mix of value and reference. They are harder to find that expected.
-
Account Deleted So, i did a little more testing ... and ... we stand partial working.
Working: aggregate 'value' type like 'character' (10a, 50A, etc.). In fact, likely applies to any data structure aggregate. (Aka, You probably get lucky with your data mix of character 'by val' and 'by ref').
Not work: primitive 'value' types like 3i0, 10i0, 20i0, 3u0, 10u0, 20u0, 12p2, 12s2, 8f, 4f, etc., are not fully implemented. I suspect these can be made to work as well, but require some time.
Do you need these other primitive types???
-
Account Deleted As far as your test ... maybe add following print so we can see the xml.
print(itool.xml_in()) itool.add(s1) print(itool.xml_out())
Also -- version toolkit is ok (same as me). However, we do not know which XMLSERVICE version you are using.
import os from itoolkit import * from itoolkit.lib.ilibcall import * itransport = iLibCall() itool = iToolKit() myxml = "<diag/>" itool.add(iXml(myxml)) # print(itool.xml_in()) itool.call(itransport) # print(itool.xml_out()) diag = itool.dict_out() if 'version' in diag: print ("version : "+diag['version'])
bash-4.3$ python testdiag.py version : XML Toolkit 1.9.2
-
Account Deleted BTW -- if you wish to change which XMLSERVICE used in iLibCall() there is an env var.
=== default -- QXMLSERV shipped by PTF from IBM Rochester === bash-4.3$ python testdiag.py version : XML Toolkit 1.9.2 === XMLSERVICE - compile from download using the crtxml CL program === bash-4.3$ export XMLSERVICE=XMLSERVICE bash-4.3$ python testdiag.py version : XML Toolkit 1.9.9.1 bash-4.3$
-
Account Deleted Ok. I know what is wrong with at least the integers ... and ... probably all other primitives. 99.9% of PASE programs are c programs. AIX/PASE c programs expect 'natural alignment' for each data type, short or 5i0 - 2 byte align, int32 or 10i0- 4 byte aligned, int64 or 20i0 - 8 byte aligned, so on. The AIX xlc compiler will do this by default so that c programs run better on power hardware (most hardware). Multiples of by 2 for those of you playing the home read along game. Our RPG compiler, of course, everything is packed to 1-byte alignment (no align). Therefore code in XMLSERVICE 'value' is 'coded', but data is not correctly aligning for primitive types. Aka, we have to teach xmlservice about c programming 'natural alignment'.
(mmm ... so obvious now ... wonder how i missed when i fooled around with xmlservice to start).
-
Account Deleted So, back to your problem. We appear to have a Schrödinger's 'live cat'/'dead cat' dealing with your test. Obviously my test is working (not accounting witty data text returned, where you will have real data). I can not see the obvious difference between 'GetProductHierarchy' and 'GETSMART'. I am curious. Please consider posting the XML (if not confidential).
print(itool.xml_in()) itool.add(s1) print(itool.xml_out())
-
Account Deleted Ok, i think i fixed pass by value for all types (not tested complete).
The new xmlservice can be downloaded.
SAVF source: YIPS latest (1.9.9.2)
Follow instructions on Yips link (installation). Use crtxml CL program to create into XMLSERVICE library. The use the python env var to call XMLSERVICE pgms/srvpgms in test XMLSERVICE library.
=== default -- QXMLSERV shipped by PTF from IBM Rochester === bash-4.3$ python testdiag.py version : XML Toolkit 1.9.2 === XMLSERVICE - compile from download using the crtxml CL program === bash-4.3$ export XMLSERVICE=XMLSERVICE bash-4.3$ python testdiag.py version : XML Toolkit 1.9.9.2 bash-4.3$
GIT source: Bitbucket Gitt
Please continue working/testing. I think we may have it (or close).
-
We are currently in the process of getting the SAVF compiled and installed. We are also double checking the parameters of the service program (to make sure our param values are correct). I will update once we are able to test. For now here's
xml_out():
<?xml version="1.0" ?><xmlservice> <error>no output</error> <xmlhint><![CDATA[ pgm error="fast" func="GetProductHierarchy" name="HFASVPR" var="prod_hier" parm by="val" io="both" var="p1" data type="10a" var="prFormat"/ /parm parm by="val" io="both" var="p2" data type="1a" var="prType" ![CDATA[L]] /data /parm parm by="val" io="both" var="p3" data type="15a" var="prValue" ![CDATA[UP]] /data /parm parm io="both" var="p1" data type= "5a" var="prChannel"/ /parm parm io="both" var="p2" data type="50a" var="prChannelDesc"/ /parm parm io="both" var="p3" data type="5a" var="prMPG"/ /parm parm io="both" var="p4" data type="50a" var="prMPGDesc"/ /parm parm io="both" var="p5" data t ype="5a" var="prClass"/ /parm parm io="both" var="p6" data type="50a" var="prClassDesc"/ /parm parm io="both" var="p7" data type="5a" var="prLine"/ /parm parm io="both" var="p8" data type="50a" var="prLineDesc"/ /parm parm io="both" var="p9" data type="15a" var="prPart"/ /parm parm io="both" var="p10" data type="30a" var="prPartDesc"/ /parm /pgm ]]></xmlhint> </xmlservice>
XML Toolkit version:
version : XML Toolkit 1.9.2
-
Account Deleted I am still working on XMLSERVICE 1.9.9.n+. Please expect a new SAVF today or tomorrow. Clarification for expectations. I am releasing xmlservice versions on YIPS because they should not effect other uses beyond 'value'. I want you to understand your task will be update xmlservice multiple times. This is what i mean by 'your help'. Again, i am trying to complete all 'value' changes by Friday. Thanks for the help.
The output of your test is unrevealing. The message "no output" is fairly useless. Great if you find an error between your python toolkit call and RPG interface (easy). However, if not, we may need to debug what is happening in XMLSERVICE. This is possible, but there are keyboard user logistics issues.
-
Account Deleted FYI -- I am still finding issues with 'value' when mixing various types. Plan for a newer version of SAVF. I will post message here when ready.
-
We're prepped to test the new version whenever it is ready. We are going to skip the current version, as you have found it to have issues still. Thanks again for all your help.
-
Account Deleted Almost made Friday. Apparently everything working except 'by value' zoned decimal. I need to talk to a colleague about register conventions 'by value' zoned decimal next week (zoned should not be a factor ... only size ... mmm). A few days, so far, not bad.
BTW -- I have not posted this latest new xmlservice anywhere yet.
-
Account Deleted Ok, give a try. Yips xmlservice.1.9.9.3 (latest).
Everything working by value except zoned decimal by value. I am not overly worried about zoned because in world of modernization, zoned decimal by value is unlikely. Aka, much more likely will be packed decimal, which works.
-
Sorry Tony, I replied to this on Tuesday but it must not have sent it seems. The new XMLToolkit is working! Thanks for getting it done so quickly, I really appreciate it. We are successfully getting results back from the service calls now. I did run into a bit of a weird snag today however, which may or may not be related to this same issue. It seems if I connect to the database with the ibm_db library, I can no longer make service calls with params by value correctly. I can, however, still make calls by reference just fine. See below:
Code being ran:
import os os.environ["XMLSERVICE"] = "XMLSERVICE" import ibm_db from itoolkit import * from itoolkit.lib.ilibcall import * DB_NAME = '*LOCAL' SQL_ATTR_DBC_SYS_NAMING = 10004 def getUser(): itool = iToolKit() itransport = iLibCall() itool.add(iSrvPgm('curuser','HFASVUT','GetCurrentUser').addRet(iData('P0User','10a',''))) itool.call(itransport) pgm_cur_user = itool.dict_out('curuser') print(pgm_cur_user) def validHierarchy(level,value): itool = iToolKit() itransport = iLibCall() svg_pgm = iSrvPgm('valid_prod_hier','HFASVPR','IsProductHierarchyValueValid') param1 = iParm('p2',{'by':'val'}) param1.add(iData('lpHierType','1a','M')) param2 = iParm('p3',{'by':'val'}) param2.add(iData('lpHierValue','15a','LVA')) svg_pgm.add(param1) svg_pgm.add(param2) svg_pgm.addRet(iData('ret_val','1a','')) itool.add(svg_pgm) itool.call(itransport) valid_prod_hier = itool.dict_out("valid_prod_hier") print(valid_prod_hier) validHierarchy("","") validHierarchy("","") validHierarchy("","") getUser() getUser() getUser() conn = ibm_db.connect(DB_NAME, None, None, {SQL_ATTR_DBC_SYS_NAMING: ibm_db.SQL_TRUE}) validHierarchy("","") validHierarchy("","") validHierarchy("","") getUser() getUser() getUser()
Response: python3 srvpgm_test.py
{'lpHierValue': 'LVA', 'ret_val': '1', 'success': '+++ success HFASVPR IsProductHierarchyValueValid', 'lpHierType': 'M'} {'lpHierValue': 'LVA', 'ret_val': '1', 'success': '+++ success HFASVPR IsProductHierarchyValueValid', 'lpHierType': 'M'} {'lpHierValue': 'LVA', 'ret_val': '1', 'success': '+++ success HFASVPR IsProductHierarchyValueValid', 'lpHierType': 'M'} {'P0User': '<REDACTED>', 'success': '+++ success HFASVUT GetCurrentUser'} {'P0User': '<REDACTED>', 'success': '+++ success HFASVUT GetCurrentUser'} {'P0User': '<REDACTED>', 'success': '+++ success HFASVUT GetCurrentUser'} {'lpHierValue': 'LVA', 'ret_val': '0', 'success': '+++ success HFASVPR IsProductHierarchyValueValid', 'lpHierType': 'M'} {'lpHierValue': 'LVA', 'ret_val': '0', 'success': '+++ success HFASVPR IsProductHierarchyValueValid', 'lpHierType': 'M'} {'lpHierValue': 'LVA', 'ret_val': '0', 'success': '+++ success HFASVPR IsProductHierarchyValueValid', 'lpHierType': 'M'} {'P0User': '<REDACTED>', 'success': '+++ success HFASVUT GetCurrentUser'} {'P0User': '<REDACTED>', 'success': '+++ success HFASVUT GetCurrentUser'} {'P0User': '<REDACTED>', 'success': '+++ success HFASVUT GetCurrentUser'}
The redacted username was the same in all calls, showing that pass by reference was working correctly. Any help would be very much appreciated.
Note: Immediately closing the connection to the database using
ibm_db.close(conn)
gives the same results as leaving it open.Thanks, Matt
-
Account Deleted Hang on to your hat, it's going to get deep in here.
First, i don't think the issue is xmlservice or ibm_db.
Without data speculation ... working as designed 'anomaly' is likely 'server mode' default set by ibm_db.
Briefly, ibm_db issues a attribute request to DB2 to enter 'server mode'. This sets CLI (ODBC) used by python ibm_db into using proxy jobs for DB2 operations outside the running python job. When these 'proxy database' jobs are running attached QSQSRVR jobs you may see them in wrkactjob (do it quickly). Python ibm_db CLI 'server mode' technique allows ibm_db to handle multiple transactions, multiple connections with same or different profiles, etc. Bluntly, this is the only way to run a 'threaded' application web server/application handling multiple requests. Even non-threaded applications like PHP use this technique to allow multiple 'pooled' persistent connections.
So after the issue of conn = ibm_db.connect(), your python job is now in 'server mode' using a proxy QSQSRVR job for ALL 'SQL' style database work. Aka, your python program is no longer one job universe, all the database work is happening in the attached QSQSRVR job. More important, DB2 'SQL' access to QTEMP moved into the QSQSRVR job.
So, QTEMP moved to another job beyond python job. So what???
Python iLibCall() is an in-job call to your called RPG program. This means the QTEMP your RPG program will see is in the python job for 'native access'. However, IMPORTANT point now, any ExecSql style access will move to the QSQSRVR job allong with all the python ibm_db.
So, problem MAY actually be your called RPG program(s) using QTEMP. Specifically theorizing 'calling' collection (or single) half-modern RPG program(s) using both ExecSql (insert into QTEMP) and 'native access' (SETLL, CHAIN, etc., to read QTEMP). Except after ibm_db.connect, QTEMP used by RPG ExecSql moved to the QSQSRVR job. Meanwhile iLibCall RPG 'native access' is still looking in the python job QTEMP (old mother Hubbard QTEMP cupboard is bare). Yuour mixed ExecSql and 'native access' program is looking at the wrong QTEMP. Aka, you RPG half-modernised program(s) world just failed.
Of course, this is a theory, but it explains the facts (Sherlock Holmes).
-
Account Deleted I forgot ... how do you fix this problem (if correct moving QTEMP mixed RPG access theory)??
option 1) Simple, switch from iLibCall to using iDB2Call. With this switch your called RPG programs will run in the same job as your ibm_db DB2 operations. Aka, half-modern RPG ExecSql and 'native access' and python ibm_db can share the same QTEMP again in the QSQSRVR job (*).
conn = ibm_db.connect(database, user, password) itransport = iDB2Call(conn)
(*) Option 2) There is a second way using iLibCall or IDB2Call (or iRestCall). If you use python itoolkit 'ipc', there will be a third job 'XTOOLKIT' where XMLSERVICE will run routed from the QSQSRVR job. Actually using itoolkit 'ipc' would work from any of the transports iLibCall, iDB2Call, iRestCall, because all will be routed to the XTOOLKIT job. In fact, you could use all these access transports at the same time, because XMLSERVICE will synchronize the requests from all sources in the XTOOLKIT job (via semaphore lock). ANYWAY, any half modern RPG ExecSql and 'native' will share the common QTEMP in the 'XTOOLKIT' job. The only odd man out will be ibm_db which will be running DB2 in QSQSRVR job (not XTOOLKIT job).
ipc (str): optional - XMLSERVICE xToolkit job route for *sbmjob ['/tmp/myunique42']
BTW -- Is your hat blowing off??? I told you this was complex ... but ... XMLSERVICE is well thought out to work in almost any configuration (no brag).
-
Well we would have NEVER found that one out on our own. Haha. And yet again the fix works great! Calling it with iDB2Call was actually even a little more performant in my testing. I appreciate the quick reply and helpful answer!
-
Account Deleted - changed status to closed
long time fixed
- Log in to comment
You will need to post your example for comment.