Как команды 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» может сказать вам, что он был упакован, но не его тип (номер заказа или номер счета-фактуры). Возможно, система команд меняет положение так же, как это было с диапазоном дат, но я не проводил этот конкретный эксперимент.

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


person Mike    schedule 02.08.2017    source источник


Ответы (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 параметров будет передан программе обработки команд точно так, как они определены в команде. Но мы не можем быть уверены в том, что следует за каким-либо параметром в памяти.

person jmarkmurphy    schedule 04.08.2017
comment
Очень интересно, попробую такой подход. Ваш оператор Смешанные списки ORDERNUM и DATERANGE появятся с целым числом элементов в качестве первых двух байтов. Если параметр смешанного списка пуст или не передан, это целое число будет содержать 0. объясняет, почему я увидел 2 в начале шестнадцатеричного дампа строки. - person Mike; 04.08.2017
comment
Единственное, что в вашем ответе не соответствует тому, что я видел, - это все параметры, которые будут переданы независимо от того, введены значения или нет, и они будут отображаться в порядке, указанном в команде. Дампы моего входного параметра показывают, что (предположительно из-за DEP NBRTRUE (* EQ 1)) неиспользуемые параметры не передаются с пустыми заполнителями, вместо этого используемый параметр смещается влево на то место, где был пропущенный. Я добавлю в свой вопрос три разных дампа, чтобы было понятнее; это можно увидеть в моих InvDate @ 4 10A и D OrderNum 4 7P 0. - person Mike; 04.08.2017
comment
Мальчик, я бы хотел, чтобы этот ответ был правильным. Как вы указываете в своей редакции, это будет лучшая техника, чем я использовал. К сожалению, когда я это делаю (избавьтесь от PASSATR, перечислите параметры по порядку, добавьте дополнительный параметр 2B, который вы не указываете для хранения listCount), измененный объект CMD по-прежнему передает только используемые параметры поэтому они смещены. Если бы я мог заставить его пройти все, как вы думаете, этот метод полностью сработал бы. - person Mike; 04.08.2017
comment
Вы пробовали, компилировали и запускали под отладкой, именно так и работает. - person jmarkmurphy; 04.08.2017
comment
Кстати, я скомпилировал и запустил этот код через Debug. Вы никогда не увидите то, что хотите, если посмотрите только на первый параметр и то, что находится за его пределами в памяти. Он никогда не выстроится в линию. Он будет работать так, как вы ожидаете, если вы используете все четыре параметра, которые вы определили в команде. - person jmarkmurphy; 04.08.2017
comment
Да, в РПГ, с дампами, чтобы выяснить, что на самом деле содержат пармы. Я не пробовал ваш CL, что, думаю, было бы следующим логическим шагом. - person Mike; 04.08.2017
comment
Вам нужно было добавить счетчик списка 2B, чтобы он выровнялся? Написал ли он дату, оставшуюся от того места, где они должны были быть? Я думаю, может быть ключевое слово, которое мне нужно добавить в CMD, чтобы заставить его оставить место, как вы говорите. - person Mike; 04.08.2017
comment
Он должен работать именно так, как есть. Вам нужно количество элементов в структурах данных ordernum и daterange, чтобы все было выровнено. Это 2-байтовое целочисленное значение. Используйте целые числа, а не старые двоичные файлы. Int (5) - это 2-байтовое целое число. - person jmarkmurphy; 04.08.2017
comment
Я создал объект CMD с указанным источником. Если вы создадите RPGLE и CMD с исходным кодом, который точно указан в списке, вы увидите, что он работает. - person jmarkmurphy; 04.08.2017
comment
Оно работает! Большое спасибо. Объект CMD по-прежнему записывает параметры в их странные перекрывающиеся посты, поэтому вы получаете мусор, если читаете параметр, который не был передан, но когда вы определяете его, как вы, компилятор выстраивает позиции параметров в правильные места. Читая указатели параметров, которые компилятор создает в дампе (_QRNL_PSTR_INVDATE, _QRNL_PSTR_ORDERNUM), вы можете видеть, что более поздние параметры все еще смещены влево и перекрывают друг друга, но RPG, очевидно, знает, как обрабатывать это право (не знаю, как). Замечательно, спасибо вам большое. - person Mike; 04.08.2017
comment
Майк, параметры не перекрывают друг друга. Они просто следуют друг за другом в хранилище. Первый параметр, переданный вашей программе, содержит информацию только для первого параметра команды. То же самое для второго, третьего и четвертого параметров. - person Barbara Morris; 05.08.2017
comment
продолжение моего предыдущего комментария ... Представьте себе программу PGMA, которая определяет структуру данных в RPG с подполями X, Y, Z. Если вы вызываете программу PGMB, передающую эти подполя как три параметра (X: Y: Z), PGMB сможет увидеть все три из X + Y + Z, если он определит только один параметр и выяснит, как найти три части. Но если бы порядок подполей в DS был изменен на Y, Z, X, PGMB был бы нарушен, если бы он неправильно пытался получить доступ ко всем трем параметрам из первого. Анализатор команд аналогичен программе PGMA. Ваша программа обработки команд похожа на PGMB. - person Barbara Morris; 05.08.2017
comment
@Mike параметры не перекрываются. Ваши ролевые игры определения есть. В ваших определениях RPG говорится, что в этой позиции всегда будет значение даты ... а также другие подобные утверждения. Но ваш объект * CMD говорит иначе, что значение даты может или не может быть передано. Ваши определения должны учитывать непереданные значения списка. Вы не можете использовать одну DS с фиксированными позициями для обработки непереданных значений списка. Каждый список требует особой обработки. - person user2338816; 17.08.2017
comment
@ user2338816 Спасибо. Это изменение точки зрения, которое отвечает на мучительную мысль, которая оставалась у меня в голове. Вкратце: не ожидайте фиксированной позиции, поскольку вы написали списки в CMD. - person Mike; 17.08.2017
comment
@ user2338816 Это немного сбивает с толку то, что вы пытаетесь здесь сказать, но на самом деле ваша программа обработки команд должна иметь один параметр для каждого PARM, определенного в источнике команды, и они должны быть в порядке, определенном в команде источник. Вы не можете попытаться получить доступ ко всем параметрам из одного параметра. Все параметры будут передаваться по порядку, независимо от того, игнорируются они командной строкой или нет. - person jmarkmurphy; 17.08.2017

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

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

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

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

ИЗМЕНИТЬ
То, что вы пытаетесь сделать, передать все параметры как один фрагмент - плохая идея. Вы можете заставить его работать сегодня, но он может выйти из строя при применении PTF или во время обновления ОС. Система предназначена для передачи индивидуальных параметров.

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

person Charles    schedule 03.08.2017
comment
Хорошо, что вы нашли эту документацию в онлайн-руководстве, поэтому она существует не только в справке редактора SEU, но, к сожалению, на самом деле не отвечает, как работает позиционирование параметров. То, что вы опубликовали, - это информация, которую я использовал в качестве фона для своего вопроса, поэтому она по-прежнему имеет ценность. - person Mike; 03.08.2017

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

person Barbara Morris    schedule 02.08.2017