2

我有一个驱动 RPGLE 程序的 CMD 命令对象。因为命令可能调用几个不同的参数,其中一些是互斥的,所以我使用RPGLE中的数据结构解析传递的参数,以便处理不同位置传递参数的不同场景。

例如,CMD 文件有:

CMD       PROMPT('Reprint Invoices and Credits')      

 PARM      KWD(ORDERNUM) TYPE(ORDER) +                 
           PROMPT('For order number:')                 

 PARM      KWD(INVDATE) TYPE(*DATE) PASSATR(*YES) +    
           PROMPT('For invoice date')                  

 PARM      KWD(DATERANGE) TYPE(DTRANGE) +              
           PROMPT('For date range:')      

 PARM      KWD(TRANSTYPE) TYPE(*CHAR) LEN(9) RSTD(*YES)      +
              DFT(*BOTH) VALUES(*INVOICES *CREDITS *BOTH)    +
              PASSATR(*YES) PROMPT('Transactions to print')   

 DTRANGE:  ELEM      TYPE(*DATE) MIN(1) PASSATR(*YES) +             
                     PROMPT('Beginning date')                       
           ELEM      TYPE(*DATE) MIN(1) PASSATR(*YES) +             
                     PROMPT('Ending date')                          

 ORDER:    ELEM      TYPE(*DEC) LEN(6) MIN(1) PASSATR(*YES) +       
                     PROMPT('Order number')                         
           ELEM      TYPE(*DEC) LEN(2) MIN(0) PASSATR(*YES) +       
                     PROMPT('Shipment number (optional)')           

           DEP       CTL(*ALWAYS) PARM(ORDERNUM INVDATE DATERANGE) +
                     NBRTRUE(*EQ 1)                                 

用户可以按各种标准打印:订单号、日期、日期范围。这三种方法只能选择一种。根据用户的选择,参数以不同的方式传递给被调用的 RPGLE 程序。

  ********************************************************************                           
  *      Parameters from CMD object INV_REPRNT                                                   

 D InputParms      DS                  TEMPLATE QUALIFIED                                        
 D  AllParms                    143A                                                             
 D  ParmType               2      2A                                        Can't find in manual 
 D                                                                          'Type' might be      
 D                                                                          a misnomer           
 D                                                                                               
 D  OrdDteAttr             3      3A                                        For attr's, see      
 D  OrderNum               4      7P 0                                      SEU help for         
 D  ShipAttr               8      8A                                        CMD PASSATR          
 D  Shipment               9     10P 0                                                           
 D  OrdInvCMAttr          21     21A                                                       
 D  OrdInvCM              22     30A                                        char  9        
 D                                                                                 
 D  InvDate@               4     10A                                               
 D  DteInvCMAttr          13     13A                                               
 D  DteInvCM              14     22A                                        char  9
 D                                                                                 
 D  BeginDateAttr         13     13A                                               
 D  BeginDate@            14     20A                                               
 D  EndDateAttr           21     21A                                               
 D  EndDate@              22     28A                                               
 D  RgeInvCMAttr          29     29A                                               
 D  RgeInvCM              30     38A                                        char  9

如您所见,后面参数的位置(如TRANSTYPE移位位置)取决于选择了哪个较早的参数。 OrdInvCM从 22 开始,DteInvCM从 14 开始,RgeInvCM从 30 开始。这不是问题,因为这个数据结构和使用它的代码能够根据我正在调用的神秘小属性选择正确的读取位置ParmType。据我所知,互联网上的 CL 手册或 SEU 编辑器中包含的帮助中没有记录此属性(PASSATR在线手册中没有相关信息)。我已经拼凑了一点ParmType's 与 pass 属性有关的行为,足以使用它,但不足以完全理解它。

一些使解析PASSATR更容易的常量(不是所有可能性):

 D Null            C                   CONST(X'00')                                            
 D Parm2           C                   CONST(X'02')                                            
 D NumSpecd        C                   CONST(X'A1')                         1010 0001          
 D NumUnspecd      C                   CONST(X'21')                         0010 0001          
 D CharQSpecd      C                   CONST(X'C5')                         1100 0101 Quoted   
 D CharQUnspecd    C                   CONST(X'45')                         0100 0101 Quoted   
 D CharUQSpecd     C                   CONST(X'85')                         1000 0101 Unquoted 
 D CharUQUnspecd   C                   CONST(X'05')                         0000 0101 Unquoted 
 D                                                                                             
 D IsSpecd         C                   CONST(X'80')                         >= 1000 0000       

我发现:

 IF P.ParmType = Null;         
   IF P.OrdDteAttr >= IsSpecd; 
     // this is a single date
   ELSE;
     IF P.BeginDateAttr >= IsSpecd;
       // this is a data range
     ELSE;
       // this is error condition I have not gotten yet
     ENDIF;
   ENDIF;
 ELSE;
   IF P.OrdDteAttr >= IsSpecd;
     // this is an order number
   ELSE;
     // this is error condition I have not gotten yet
   ENDIF;
 ENDIF;

换句话说,ParmType当参数是日期或日期范围时,它的十六进制值为“00”。当ParmType参数是“订单号”的打包 *DEC (6P 0) 时,它的十六进制值为“02”。

我想了解如何将此ParmType值设置为给定数字,以便我可以稳健地编写可以接受各种参数组合的程序。我也没有看到为什么数据范围字段从 14 开始而不是像单个日期那样从 4 开始的特定原因。我能够利用这一事实进行必要的区分,但我不知道指挥系统是否故意这样做因为它看到我有两种相同数据类型的可能性,或者这只是一个幸运的突破,不能保证会发生。如果我想添加一个额外的打包参数作为选择,就会出现类似的问题,比如发票号码。'A1' 的 'PASSATR' 十六进制值可以告诉您它已包装,但不能告诉您是哪种类型(订单号或发票号)。可能是命令系统移动位置类似于它对日期范围所做的那样,但我没有运行那个特定的实验。

简而言之,是否有关于命令如何构建其参数列表的文档或至少推导出算法,以便可以预测这些字段将包含什么以及它们将位于何处?

4

3 回答 3

4

无论是否输入值,所有参数都将被传递,并且它们将按照命令中提供的顺序出现。

PASSATR不应该是需要的,把它排除在外。

CMD       PROMPT('Reprint Invoices and Credits')

  PARM      KWD(ORDERNUM) TYPE(ORDER) +
            PROMPT('For order number:')

  PARM      KWD(INVDATE) TYPE(*DATE) +
            PROMPT('For invoice date')

  PARM      KWD(DATERANGE) TYPE(DTRANGE) +
            PROMPT('For date range:')

  PARM      KWD(TRANSTYPE) TYPE(*CHAR) LEN(9) RSTD(*YES) +
            DFT(*BOTH) VALUES(*INVOICES *CREDITS *BOTH)  +
            PROMPT('Transactions to print')

  DTRANGE:  ELEM      TYPE(*DATE) MIN(1) +
                      PROMPT('Beginning date')
            ELEM      TYPE(*DATE) MIN(1) +
                      PROMPT('Ending date')

  ORDER:    ELEM      TYPE(*DEC) LEN(6) MIN(1) +
                      PROMPT('Order number')
            ELEM      TYPE(*DEC) LEN(2) MIN(0) +
                      PROMPT('Shipment number (optional)')

            DEP       CTL(*ALWAYS) PARM(ORDERNUM INVDATE DATERANGE) +
                      NBRTRUE(*EQ 1)

混合列表ORDERNUMDATERANGE并将以整数个元素作为前两个字节出现。如果混合列表参数为空或未传递,则此整数将包含 0。

以下是如何在 CL 中编写命令处理程序

PGM        PARM(&ORDERNUM &INVDATE &DATERANGE &TRANSTYPE)

         DCL        VAR(&ORDERNUM) TYPE(*CHAR)
         DCL        VAR(&ONELMCNT) TYPE(*INT) STG(*DEFINED) +
                      LEN(2) DEFVAR(&ORDERNUM 1)
         DCL        VAR(&ONORDER) TYPE(*DEC) STG(*DEFINED) LEN(6 +
                      0) DEFVAR(&ORDERNUM 3)
         DCL        VAR(&ONSHIPNO) TYPE(*DEC) STG(*DEFINED) +
                      LEN(2 0) DEFVAR(&ORDERNUM 7)

         DCL        VAR(&INVDATE) TYPE(*CHAR) LEN(7)

         DCL        VAR(&DATERANGE) TYPE(*CHAR)
         DCL        VAR(&DRELMCNT) TYPE(*INT) STG(*DEFINED) +
                      LEN(2) DEFVAR(&DATERANGE 1)
         DCL        VAR(&DRBDATE) TYPE(*CHAR) STG(*DEFINED) +
                      LEN(7) DEFVAR(&DATERANGE 3)
         DCL        VAR(&DREDATE) TYPE(*CHAR) STG(*DEFINED) +
                      LEN(7) DEFVAR(&DATERANGE 10)

         DCL        VAR(&TRANSTYPE) TYPE(*CHAR) LEN(9)

         if (&onelmcnt *ne 0) do
           /* ORDERNUM parameter has been entered */
         enddo

         if (&invdate *ne '0000000') do
           /* INVDATE parameter has been entered */
         enddo

         if (&drelmcnt *ne 0) do
           /* DATERANGE parameter has been entered */
         enddo

         if (&transtype *ne ' ') do
         enddo

done:   endpgm 

注意混合列表 ( ELEM) 参数的结构。如果列表中的元素数为 0,则只有这些结构中的元素计数&xxelmcnt字段有效。还要注意,这些字段将始终包含 0 或 2(每个列表中定义的元素数)。ORDERNUM如果提供该参数,即使发货人编号留空,该参数也将包含 2。在这种情况下,为托运人编号传递的值将为 0。

您可以在 RPG 程序中以类似的方式处理它:

   ctl-opt Main(testcmd);

   dcl-ds ordernum_t qualified template;
     elements      Int(5);
     order         Packed(6:0);
     shipper       Packed(2:0);
   end-ds;

   dcl-ds daterange_t qualified template;
     elements      Int(5);
     begindt       Char(7);
     enddt         Char(7);
   end-ds;

   dcl-proc testcmd;
     dcl-pi *n ExtPgm('TESTCMD');
       ordernum      LikeDs(ordernum_t) const;
       invdate       Char(7) const;
       daterange     LikeDs(daterange_t) const;
       transtype     Char(9) const;
     end-pi;

     if ordernum.elements <> 0;
       // parameter has been entered
     endif;

     if invdate <> '0000000';
       // parameter has been entered
     endif;

     if daterange.elements <> 0;
       // parameter has been entered
     endif;

     if transtype <> '';
       // parameter has been entered
     endif;

     return;
   end-proc;

是一些关于如何处理混合列表参数的文档。手册中围绕它的是简单列表的描述,以及列表中的列表(非常复杂)。


编辑正如查尔斯指出的那样,您试图在示例中将参数值作为单个块访问。这几乎肯定会引起混淆,因为除了程序中定义的参数引用之外,没有关于如何将参数加载到内存中的(公共)定义。参数通过引用传递,由调用程序确定它们在内存中的位置。假设每个参数在物理上与前一个参数相邻可能是一个危险的假设。访问给定参数的唯一安全方法是使用其单独的参数引用。尝试从参数 1 的引用中访问参数 2 是个坏主意。即使一次有效,也不一定总是有效。如您所见,命令对象根据用户键在内存中移动内容。

由于我们知道上面的命令定义了 4 个参数,即 4 个PARM元素,我们可以确信 4 个参数中的每一个都将完全按照命令中定义的方式传递给命令处理程序。但是,我们不能对内存中任何参数之后的内容有信心。

于 2017-08-04T13:31:44.187 回答
2

PASSATR 记录在这里Pass attribute byte (PASSATR)

*YES
属性字节与参数一起传递。属性字节有两个字段:

  1. 属性字节的最左边位指示是否指定了值。如果最左边的位是'0'B,则传递给命令处理程序的值是默认值并且没有在命令字符串中指定。如果最左边的位是'1'B,则传递给命令处理程序的值在命令字符串中指定。
  2. 其余七位描述当为值类型 (TYPE) 参数指定 *CHAR 时传递给命令处理程序的值。

Attribute Description ---------- -------------------------------------- '0000010'B Meets *NAME rules, like A_B '0000100'B Meets GENERIC rules, like AB '1000101'B Quoted character string, like 'A B' '0000101'B Unquoted character string, like 5A '1001000'B Logical constant, '0' or '1' '0001100'B Hexadecimal value, like X'C1C2' '0100001'B Unsigned numeric value, like 5 '0101001'B Unsigned numeric with decimal point, like 5.2 '0110001'B Signed numeric value, like -5 '0111001'B Signed numeric with decimal point, like -5.2

还要查看未指定时通过的值 (PASSVAL),它记录在 PASSATR 下方。

未指定时传递的值 (PASSVAL)
指定是否将值传递给此参数的命令处理程序。*NULL 如果参数是常量参数(其中已为常量值 (CONSTANT) 参数指定值的参数,或已为值的类型指定 *ZEROELEM 或 *NULL 的参数( TYPE)参数,或由所有常量 ELEM 或 QUAL 语句定义的列表/限定名称)。如果在返回值 (RTNVAL) 参数上指定了 *YES,或者为所需最小值 (MIN) 参数指定的值大于零,*NULL 也无效。DEP 语句或其他 PARM 语句的 REL 和 RANGE 关键字可能不引用使用 *NULL 定义的参数的值。

如果您PASSVAL未指定参数为 *NULL,您应该能够在 RPGLE 中将它们定义为OPTION(*OMIT) ,然后检查if %addr(myOptParm) <> 0;

编辑
你想要做的,将所有参数作为一个块传递是一个坏主意。您可能今​​天就可以使用它,但它可能会在应用 PTF 或操作系统升级期间中断。该系统旨在传递各个参数。

只需将它们全部传递给您的 RPG 程序并检查实际使用的内容。

于 2017-08-03T13:45:04.217 回答
0

我隐约想起 Bob Cozzi 的一篇文章,其中谈到了 PASSATR 属性。也许它会有所帮助...... https://www.mcpressonline.com/programming/rpg/retrieving-user-space-data

于 2017-08-02T22:52:19.660 回答