Как команды CL создают свои точные списки параметров?

У меня есть объект команды CMD, который управляет программой RPGLE. Поскольку команду можно вызывать с несколькими различными параметрами, некоторые из которых являются взаимоисключающими, я анализирую передаваемый параметр, используя структуру данных в 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Поведение по отношению к атрибутам 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 имеет шестнадцатеричное значение "02", когда параметр является упакованным *DEC (6P 0) для "Номер заказа".

Я хотел бы понять, как это ParmType значение устанавливается на заданное число, поэтому я могу надежно писать программы, которые могут принимать различные комбинации параметров. Я также не вижу конкретной причины, по которой поля диапазона данных начинаются с 14, а не с 4, как одна дата. Мне удалось использовать этот факт для проведения необходимого различия, но я не знаю, делала ли система команд это намеренно, потому что увидела, что у меня есть две возможности одного и того же типа данных или это просто счастливый случай, которого нет гарантированно произойдет. Аналогичный вопрос возникает, если я хочу добавить дополнительный упакованный параметр в качестве выбора, скажем, номер счета. Шестнадцатеричное значение 'PASSATR' для 'A1' может сказать вам, что оно было упаковано, но не какой тип (номер заказа или номер счета). Может случиться так, что система команд смещает позицию, аналогичную той, что была с диапазоном дат, но я не проводил этот конкретный эксперимент.

Короче говоря, есть ли документация или хотя бы выведенные алгоритмы о том, как команды строят свои списки параметров, чтобы можно было предсказать, что эти поля будут содержать и где они будут расположены?

3 ответа

Решение

Все параметры будут переданы независимо от того, введены значения или нет, и они будут отображаться в порядке, указанном в команде.

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)

Смешанные списки ORDERNUM а также DATERANGE появится с целым числом элементов в качестве первых двух байтов. Если параметр смешанного списка пуст или не передан, это целое число будет содержать 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) параметры. Только количество элементов &xxelmcnt поля в этих структурах действительны, если количество элементов в списке равно 0. Также обратите внимание, что они всегда будут содержать 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;

Вот некоторая документация о том, как обрабатываются параметры смешанного списка. в руководстве его окружают описания простых списков и списки в списках (очень сложные).


Отредактируйте, как указал Чарльз, вы пытаетесь получить доступ к значениям параметров в виде отдельного блока в вашем примере. Это почти гарантированно вызовет путаницу, поскольку не существует (публичного) определения того, как параметры загружаются в память, кроме ссылок на параметры, определенных в программе. Параметры передаются по ссылке, и вызывающая программа определяет, где они находятся в памяти. Предполагать, что каждый параметр физически соседствует с предыдущим параметром, может быть опасным предположением. Единственный безопасный способ получить доступ к данному параметру - использовать его отдельную ссылку на параметр. Это плохая идея, чтобы попытаться получить доступ к параметру 2 из ссылки параметра 1. Даже если работает один раз, это не всегда будет работать. Как вы уже видели, объект команды перемещает вещи в памяти в зависимости от того, какие пользовательские ключи.

Поскольку мы знаем, что команда выше определяет 4 параметра, то есть 4 PARM элементы, мы можем быть уверены, что каждый из 4 параметров будет передан программе обработки команд в точности так, как они определены в команде. Но мы не можем быть уверены в том, что следует за каким-либо параметром в памяти.

PASSATR здесь задокументирован Байт атрибута передачи (PASSATR)

*ДА
Байт атрибута передается с параметром. Байт атрибута имеет два поля:

  1. Крайний левый бит атрибута байта указывает, было ли указано значение. Если крайний левый бит равен 0, значение, передаваемое программе обработки команд, является значением по умолчанию и не было указано в командной строке. Если крайний левый бит равен "1", значение, переданное программе обработки команд, было указано в командной строке.
  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). *NULL также недопустимо, если *YES было указано в параметре Возвращаемое значение (RTNVAL), или если значение, указанное для параметра Минимальные требуемые значения (MIN), больше нуля. Оператор DEP или ключевые слова REL и RANGE других операторов PARM могут не ссылаться на значение параметра, определенного с помощью *NULL.

если ты PASSVAL неопределенные параметры как *NULL, вы должны быть в состоянии определить их в RPGLE как OPTION(*OMIT) а затем проверьте if %addr(myOptParm) <> 0;

РЕДАКТИРОВАТЬ
То, что вы пытаетесь сделать, передать все параметры как один кусок - плохая идея. Вы могли бы заставить это работать сегодня, но это могло бы сломаться с применением PTF или во время обновления ОС. Система предназначена для передачи индивидуальных параметров.

Просто передайте их все в вашу программу RPG и проверьте, что на самом деле использовалось.

Я смутно вспомнил статью Боба Коззи, в которой говорилось об атрибуте PASSATR. Может быть, это поможет... https://www.mcpressonline.com/programming/rpg/retrieving-user-space-data

Другие вопросы по тегам