- PVSM.RU - https://www.pvsm.ru -
Здравствуйте, уважаемые читатели. Меня зовут Владимир Лебедев, я работаю в Альфа-Банке и занимаюсь тем, что пытаюсь максимально упростить жизнь разработчиков АБС Equation, занимающихся разработкой приложений под операционную систему IBM i. [1]
Сразу скажу, что литературный слог – точно не мой конек. Я больше про исследования и скрупулезное изучение технических возможностей, про поиск инженерных решений. Однако у нас в сообществе разработчиков IBMi в банке принято, что по итогам года выбираются законченные реализации, которыми мы делимся с читателями и за пределами Альфа-Банка. В этом году в их число попала и моя работа.
Также скажу, что пытливый читатель не найдет в статье сногсшибательных прорывов и идей, которые кардинально меняют мир вокруг. Скорее, работу можно рискнуть сравнить с процессом доказательства теоремы Ферма. Известно, что Пьер Ферма еще в 1637 году сформулировал свою великую теорему. Почти четыре столетия ученые пытались расколоть этот орешек. Но удалось это сделать только в 1994 году Эндрю Уайлсу, а в 2016 году этот гениальный норвежец получил за дело своей жизни Абелевскую премию [2]. Доказательство теоремы Ферма не несет за собой исключительной практической ценности или стремления к славе и успеху, но в процессе решения задачи были найдены интересные идеи, выросли целые поколения ученых.
Мой скромный (но, похвалю себя =), честный и кропотливый) труд — он не про решения во фронтовом программном обеспечении Альфа-Банка. Хотя, надо признать, что мои коллеги здесь крайне преуспели, и банк занимает свое заслуженное место в рейтингах. Мой труд про исследования, которые являются неотъемлемой историей любого профессионального сообщества, стремящегося к саморазвитию на всех уровнях.
Итак, начнем. ОС IBM i и ее предшественницы — aka AS/400 [3] — известны тем, что все ее интерактивные приложения до сих пор работают через так называемый зеленый экран (green screen или GS). Выглядит это примерно так:
[4]
Пример Green Screen-а
Если вы разработчик под IBMi, то, несомненно, согласитесь, что разработка такого рода приложений вызывает неудобства (а, если прямо сказать, то большую головную боль и внутреннее раздражение). Даже для продвинутых разработчиков (особенно для них) привыкших к разработке современных пользовательских интерфейсов на базе веб-технологий.
Надо признать, что при всем неудобстве интерактивные приложения с консольным GS до сих пор очень нужны многим зрелым, многолетним предприятиям. И Альфа-Банк не исключение. Такую «зеленую» картинку можно наблюдать на мониторе большинства сотрудников бэк-офиса. Green Screen для IBM i — это как SSH и SHELL для Unix-а. К слову, SSH и SHELL есть в IBM i. Но здесь вы попадаете в особый runtime, именуемый PASE [5], со своими правилами игры.
Важно отметить, что платформа IBMi поддерживает 2 базовых программных модели – уже устаревшую OPM [6] (Original Program Model) и активно развиваемую на данный момент ILE [7] (Integrated Language Environment). Если кратко, то под программной моделью здесь понимается совокупность решений операционной системы по квотированию ресурсов, возможностям кросс-языковых реализаций и т.д. Но при всех преимуществах новой ILE-концепции [8] эффективность рекомендуемых IBM-ом способов разработки Green Screens оставляет желать лучшего.
Я уже упомянул, что разработка интерактивных приложений под IBM i с использованием традиционных подходов — занятие не самое увлекательное. А уж детальный рассказ про эту разработку вызовет зевоту или бурное негодование среди видавших виды. Однако, я не могу опустить это описание, так как это важно для общего понимания проблематики. Насколько подробно следить за мыслью — решайте сами.
Стоит обратить внимание, что IBMi является одной из самых ранних объектных операционных систем. В отличие от традиционных Unix/Windows, здесь нет файлов в обычном понимании. Система оперирует специализированными объектами разных по назначению типов – файлы (БД и интерфейсы I/O), программы (исполняемый код), профайлы (учетные записи) и т.д. Внутри этих объектов прячутся как сами данные (согласно назначению), так и все необходимые метаданные, позволяющие правильно интерпретировать объект и правильно им воспользоваться.
Некоторые типы объектов имеют подтипы. Например, объект типа *FILE, подтипа PF – т.н. физический файл — инкапсулирует внутри себя все необходимое для работы с ним, как с таблицей БД. А тип *FILE c подтипом LF (логический файл) реализует индекс таблицы.
Обратим внимание на важный для описываемой истории подтип объекта *FILE — дисплейный файл (DSPF). Его предназначение как раз в описании пользовательского интерфейса (терминальный green screen). Интерактивное взаимодействие программы и пользователя выполняется именно через этот объект.
Основное содержимое дисплейного файла — форматы записи. Каждый такой формат записи определяет шаблон внешнего вида пользовательского экрана или его части. Т.е. типизирует поля (например, только для вывода или ввода/вывода, текст или число), фиксирует их размерность и расположение на экране.
Поле — это наименьшая единица данных, которая распознается и обрабатывается системой управления данными. Форматы записи в дисплейных файлах определяются с помощью спецификации описания данных (DDS).
Описание файла описывает данные на трех разных уровнях:
Описание уровня поля позволяет дать системе подробные характеристики поля, такие как расположение на экране, допустимый тип данных, представление поля при операциях ввода и вывода, тип поля, возможность редактирования, выравнивание данных в поле и прочее.
Запись — это упорядоченный набор из одного или нескольких полей. Описание уровня записи позволяет вам сказать системе, как выглядит конкретная запись или ее формат записи. Запись является минимальной единицей обмена данными между системой и прикладной программой, независимо от наличия полей.
Описание уровня файла — это описание, которое применяется к файлу в целом. Для файла дисплея вы можете указать используемые форматы записи, дисплейную станцию, набор графических символов.
Формирование исходного файла дисплея осуществляется с помощью кодирования DDS.
Пример дисплейного файла в редакторе SEU
Да, печально, но дисплейный файл имеет только позиционную форму записи. Других форм на 2020-й год нет. Однако, давайте все же пройдем не самый увлекательный путь, которым ходили и в прошлом веке, чтобы двигаться дальше и искать пути решения.
Пример примером, давайте рассмотрим задачку.
Создадим дисплейный файл для некоторой программы WINDEMOR:
Пример окна с выводом сообщения
A*----------------------------------------------------------------
A* System Name - PUB400
A*
A* File Name - WINDEMOD
A* File Description - Демонстрация работы с окном
A* File Type - Display
A*L Level - 100
A*
A* Creation Date - 2020/02/25
A* Last Amendment Date - 2020/02/25
A*----------------------------------------------------------------
A* Change Control
A* ~~~~~~~~~~~~~~
A* Date Initials Amendment
A* ~~~~ ~~~~~~~~ ~~~~~~~~~
A* 2020/02/25 USRXXX Создано
A*
A*----------------------------------------------------------------
A* Purpose
A* ~~~~~~
A* Демонстрация работы с экраном
A*----------------------------------------------------------------
A* Indicators
A* ~~~~~~~~~~
A* Number Description
A* ~~~~~~ ~~~~~~~~~~~
A* 01-04 Attribute control indicators (message to display)
A* 10-13 External account number indicators
A* 71 Modify data tag
A* 73 Errors to display
A* 74 Warnings to display
A* 76 Protect branch
A* 83 Load message subfile
A* 90 Keyboard key pressed
A* 91 Rollup
A* 92 Rolldown
A* 93 Help key pressed
A* 94 Home key pressed
A* 95 Clear key pressed
A*----------------------------------------------------------------
A DSPSIZ(24 80 *DS3)
A REF(*LIBL/EQFREF)
A INDARA
A PRINT
A VLDCMDKEY(90)
A HOME(94)
A CLEAR(95)
A ALTHELP
A HELP
A CF02
A CF03
A CF04
A CF05
A CF06
A CF09
A CF10
A CF11
A CF12
A CF13
A CF14
A CF15
A CF16
A CF17
A CF18
A CF19
A CF20
A CF21
A CF22
A CF23
A CF24
A*----------------------------------------------------------------
A R HEADER
A WINDOW(7 10 7 59)
A BLINK
A LOCK
A KAPPG7 7A O 1 1COLOR(BLU)
A KAPLVL R O 1 9REFFLD(FPL)
A COLOR(BLU)
A ZLTITL R O 1 13REFFLD(ONM)
A DSPATR(HI)
A ZUDATE R O 1 49REFFLD(DAZ)
A*
A*----------------------------------------------------------------
A R WINDEMODA
A WINDOW(HEADER)
A CHANGE(84)
A RTNCSRLOC(&ZHRFMT &ZHFLD)
A BLINK
A INVITE
A OVERLAY
A PUTOVR
A RMVWDW
A*
A ZHFLD 10A H
A ZHRFMT 10A H
A 01N02 3 15'Внештатная ситуация №1'
A DSPATR(HI)
A N01N02 3 17'Внештатная ситуация №2'
A DSPATR(HI)
A N01 02 3 24'Ошибка вызова'
A DSPATR(HI)
A 6 1'F3=Выход'
A COLOR(BLU)
A*
A*----------------------------------------------------------------
A R OPTION
A CHANGE(84)
A BLINK
A LOCK
A OVERLAY
A PUTOVR
A N83 ERASE(MSGSFL)
A WINDOW(HEADER)
A ZLCMD 58A O 6 1COLOR(BLU)
A OVRDTA
A*
A*----------------------------------------------------------------
A R MSGSFL SFL SFLMSGRCD(06)
A FLDKEY SFLMSGKEY
A FLDPGM SFLPGMQ
A*
A*
A R MSGCTL SFLCTL(MSGSFL)
A WINDOW(HEADER)
A BLINK
A LOCK
A OVERLAY
A PUTOVR
A SFLDSP
A SFLINZ
A 83 SFLEND
A SFLSIZ(0020)
A SFLPAG(0001)
A FLDPGM SFLPGMQ(10)
A*
A*----------------------------------------------------------------
A R CLEAN
A ASSUME
A 24 2' '
В операционной системе существуют функции-расширения стандартного API для С/С++ или встроенные в языки инструкции, которые позволяют открывать дисплейные файлы и взаимодействовать с ними.
Важно отметить, что строки «R HEADER», «R WINDEMODA», «R OPTION» и другие определяют то, что называется записью дисплейного файла, или по-другому Record-форматом. Этот формат определяет структуру буфера, через который будет происходить обмен данными между эмулятором терминала пользователя и программой, которая работает с «дисплейником».
Если программе необходимо интерактивное взаимодействие с терминалом пользователя, то она должна записать в Record-формат, что хочет вывести в поля экрана (если программа ничего не хочет выводить на экран, ей все равно нужно записать пустые бланковые поля). После этого необходимо произвести чтение Record-формата, при этом управление передается операционной системе, которая выводит пользователю статическое содержимое (например, фраза «Внештатная ситуация №1» в позицию 3/15»).
Под спойлером можете посмотреть код программы на языке IBM RPG, который демонстрирует работу с данным окном и реализует логику приложения.
/TITLE Демонстрация работы с окном
*---------------------------------------------------------------------
* System Name - PUB400
*
* Program Name - WINDEMOR
* Program Title - Демонстрация работы с окном
*
*L Level - 100
*
* Creation Date - 25FEB20
* Last Amendment Date - 25FEB20
*---------------------------------------------------------------------
*P Purpose
*P ~~~~~~~
*P Демонстрация работы с окном
*P
*---------------------------------------------------------------------
*H Change Control
*H ~~~~~~~~~~~~~~
*H Project Programmer Date Level
*H ~~~~~~~ ~~~~~~~~~~ ~~~~ ~~~~~
*H WINDEMO USERXXX 25FEB20 100
*H Создано
*---------------------------------------------------------------------
/COPY EQCPYLESRC,HSPEC
H DFTNAME(WINDEMOR) WINDEMO
*
*---------------------------------------------------------------------
* FILES
*---------------------------------------------------------------------
FWINDEMOD CF E WORKSTN INFSR(*PSSR) USROPN
*
*---------------------------------------------------------------------
* DATA STRUCTURES
*---------------------------------------------------------------------
*
D @EM S 37 DIM(20)
D #Check1 S 1A
*---------------------------------------------------------------------
* PRE-INITIALISED DATA STRUCTURES
*---------------------------------------------------------------------
*
* KAPROG data structure
D KAPROG DS
D MISYS 1 6 INZ('MISYS ')
D KAPLVL 8 10 INZ('100')
D KAPPGM 12 17
D KAPPG7 12 18
*
/COPY EQCPYLESRC,DSJOBE_A
/COPY EQCPYLESRC,DSSYSE
*
/COPY EQCPYLESRC,DSEPMS
/COPY EQCPYLESRC,DPGMDS Data structure for progra
/COPY EQCPYLESRC,DMSGDS_A DS for dump message *
*---------------------------------------------------------------------
* NAMED CONSTANTS
*---------------------------------------------------------------------
/COPY EQCPYLESRC,STDCNST Standard program constant
*---------------------------------------------------------------------
/COPY EQCPYLESRC,STDFLDS
/COPY EQCPYLESRC,CMRINZS
/COPY EQCPYLESRC,CMRMAIN
/COPY EQCPYLESRC,CMRABRT
/COPY EQCPYLESRC,CMRPSSR
?*---------------------------------------------------------------------
?* C SPEC START
?*---------------------------------------------------------------------
C CSpecStart TAG
/COPY EQCPYLESRC,MAIN
*
*---------------------------------------------------------------------
* PARAMETER AND KEY LISTS
*---------------------------------------------------------------------
*
* Parameter lists
C *ENTRY PLIST
C PARM @Exit 1
C PARM @Chk1 1 № сообщ. для вывода
*=====================================================================
* MAINLINE
*=====================================================================
C MOVE #NO @Exit
C If %PARMS = 2
C Eval #Check1 = @Chk1
C Else
C Eval #Check1 = *Blanks
C EndIf
*
* Если #Check1 = 'A' => первое сообщение, #Check2 = 'B' => второе сообщение
* иначе - сообщение об ошибке вызова
*
C If #Check1 <> 'A' And #Check1 <> 'B'
C* если неверный параметр, устанавливаем флаг завершения
C MOVE #YES @Exit
C EndIf
C*
C MOVE #NO #EOP 1
*
C IF not %OPEN(WINDEMOD)
C OPEN WINDEMOD
C ENDIF
C Z-ADD *ZERO S3
C EXSR Z000
*
C DOW #EOP = #NO
C EXSR B000
C ENDDO
*
* Return to caller
C EXSR C000
*
C RETURN
*=====================================================================
* SUBROUTINES
*=====================================================================
*---------------------------------------------------------------------
C *INZSR BEGSR
/COPY EQCPYLESRC,INZSR
*---------------------------------------------------------------------
*
*S Program initialization
*
*---------------------------------------------------------------------
*
C *Dtaara Define DAJOBCTLE DSJOBE
C *Dtaara Define DASYSCTL DSSYSE
C IN *DTAARA
*
C MOVEL(P) @PGMID @PGM
C MOVEL(P) @PGMID FLDPGM
C MOVEL(P) @PGMID KAPPG7
*
C MOVE $ZDATE ZUDATE
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C B000 BEGSR
*---------------------------------------------------------------------
*
C READ WINDEMODA
*
C Z-ADD *ZERO S3
*
C MOVE *BLANKS #CMDKY 2
C MOVE '0' #TAGNO 1
*
C *IN90 CASEQ *ON B100
C END
C #TAGNO CABEQ '2' B000XT
*
*
B001 C IF #EOP = #NO
C EXSR Z000
E001 C ENDIF
*
C B000XT ENDSR
/EJECT
*---------------------------------------------------------------------
C B100 BEGSR
*---------------------------------------------------------------------
*
B001 C IF *INKC = *ON
C MOVEL(P) '03' #CMDKY
C MOVE #YES #EOP
C MOVE '2' #TAGNO
E001 C ENDIF
*
*
C MOVEL(P) 'KSM0807' DSEPMS
C EXSR USA10R
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C Z000 BEGSR
*---------------------------------------------------------------------
*
* Write header format
C WRITE HEADER
*
C Z-ADD *ZERO S3
*
C MOVE #YES @CLRQ
C CALLB 'UTM14C'
C PARM @PGM 10
C PARM @EM
C PARM S3 2 0
C PARM @CLRQ 1
*
* Write detail format
S001 C SELECT
W001 C WHEN #Check1 = 'A'
C MOVE *ON *IN01
C MOVE *OFF *IN02
W001 C WHEN #Check1 = 'B'
C MOVE *OFF *IN01
C MOVE *OFF *IN02
B001 C OTHER
C MOVE *OFF *IN01
C MOVE *ON *IN02
E001 C ENDSL
C WRITE WINDEMODA
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C C000 BEGSR
*---------------------------------------------------------------------
*
B001 C IF %OPEN(WINDEMOD)
C CLOSE WINDEMOD
E001 C ENDIF
*
C RETURN
*
C ENDSR
/EJECT
*---------------------------------------------------------------------
C USA10R BEGSR
*---------------------------------------------------------------------
*
C MOVEL(P) @ERM @MSGID
C MOVEL(P) @PMALL @MSGDA
*
C CALLB 'UTM02C'
C PARM @MSGID 7
C PARM @MSGDA 30
C PARM @MSGDT 132
C PARM @MSGSV 2 0
*
B001 C IF S3 < 20
C ADD 1 S3
C EVAL @EM(S3) = DSEPMS
E001 C ENDIF
*
C MOVE *BLANKS DSEPMS
*
C ENDSR
?*
/COPY EQCPYLESRC,PSSR_B
*
*---------------------------------------------------------------------
* COMPILE TIME ARRAY DATA
*---------------------------------------------------------------------
*
DDS представляет широкие возможности по работе с экраном дисплея и взаимодействия с прикладной программой, реализуя возможности протокола обмена данными терминала 5250 для IBM i. Несмотря на то, что кодирование файлов дисплея является фактически стандартом для взаимодействия с экраном, этот способ, на мой взгляд, имеет и ряд недостатков:
Итак, мы с вами прошлись по головной боли. Посмотрели каких «милых позиционных монстров» разработчикам приходится, скрепя сердце, создавать для реализации даже простейших задач, при этом понимая, что к созданным изваяниям придется еще не раз возвращаться и вносить изменения по новым требованиям к задаче. Настало время осознать всю тяжесть темного прошлого и посмотреть в светлое будущее.
Есть ли альтернатива DDS? Безусловно, есть. Внимание привлек интерфейс (API) менеджера динамического экрана (Dynamic Screen Manager — DSM), реализующий протокол потока данных терминала 5250 для IBM i.
Давайте поподробнее и подотошнее познакомимся с ним. Далее разберемся, какие же выгоды можно извлечь по сравнению с DDS. Читать именно этот пункт подробно или нет — зависит от того, собираетесь ли вы использовать результаты или захотите погрузиться в реализацию.
DSM API представляет собой набор экранных интерфейсов ввода/вывода, которые обеспечивают возможности динамического создания и управления экранами для языков программирования ILE. Поскольку DSM-интерфейсы являются связанными, они доступны только для ILE-программ.
DSM API обеспечивает альтернативу существующему способу определения представления экрана вне программы посредством кодирования в DDS или UIM. Вместо этого программисты могут использовать в своих программах последовательность обращений к DSM для динамического определения и управления представлением экрана. В отличие от статических методов, интерфейс DSM обеспечивает приложениям, нуждающимся в более динамичном управлении экраном, необходимую гибкость.
Поддержка, предоставляемая DSM, варьируется от низкоуровневых интерфейсов для непосредственных манипуляций с экраном до работы с окнами.
DSM API состоит из следующих функциональных групп:
Казалось бы, простейшая и для понимания, и для применения вещь. Однако здесь тоже кроются подводные камни. Многое зависит не только от правильной интерпретации имеющейся документации, но и от накопленного опыта хождения по схожим граблям.
В общем случае вывод информации на экран не отличается от других подобных устройств. Информация выводится блоками из одного или более символов цветом предшествующего ей атрибута. Атрибут определяет цвет всех выводимых за ним символов, пока не встретится следующий атрибут, и так далее. При отсутствии атрибутов применяется атрибут зеленого цвета. Каждый атрибут занимает одну позицию и на экране выглядит как пустая позиция (пробел).
Например:
X — символ
R — атрибут красного цвета
G — атрибут зеленого цвета
B — атрибут синего цвета
На экране это может выглядеть так:
Ввод информации осуществляется с помощью полей ввода, создаваемых и обслуживаемых терминалом. Количество полей ввода ограничено, допускается одновременное существование не более 256 полей. Характеристиками полей (цветом лидирующего атрибута, длиной, типом и способом ввода данных, выравниванием и т.д.) можно управлять. Имеется набор клавиш перемещения внутри полей и между полями, а также набор клавиш завершения редактирования. Поля могут быть простыми и композитными, т.е. состоящими из нескольких простых полей и ведущими себя как одно целое. Поля предваряются лидирующим атрибутом, определяющим цвет поля, и завершаются атрибутом конца поля (всегда “зеленый на черном”).
Так выглядят на экране поля ввода:
Если вы разработчик и собираетесь «залезть» в DSM, то при работе с DSM важно понимать структуру и порядок формирования окон, а также порядок вывода и отображения информации на экране. Невнимание к этому аспекту может привести к удивительным или даже весьма комичным ошибкам, которые легко устраняются, когда педантично все разложишь по полочкам.
Окном является прямоугольная область экрана, не выходящая за его пределы. Положение и размеры окна определяются координатами левого верхнего угла окна и количеством строк и столбцов окна. Окно может иметь или не иметь рамку. В наличии могут быть не все элементы, составляющие окно.
Окно может не иметь рамки, но если она есть, то возможно определение атрибутов рамки. Атрибуты рамки одинаковы и определяют ее цвет. Лидирующий атрибут окна определяет цвет символов, выводимых в окно. Правый атрибут завершения – это не жестко определенный атрибут, а признак необходимости расчета завершающего атрибута и место для его вывода на экран.
Пример структуры окна
Окно с атрибутами
Так выглядит окно, созданное программой с учетом представленной структуры. Зеленое окно с синей рамкой на белом экране. Для наглядности выбраны инверсные цвета. Хочу напомнить, что символы имеют цвет, определяемый предваряющим их атрибутом. Цвет символов сохраняется, пока не встретится следующий атрибут. На этой картинке правый атрибут завершения окна во всех строках имеет значение “белый инверсный”. Таким образом восстанавливается цвет экрана.
Наличие атрибутов обеспечивает правильное закрашивание рамки и информации в окне. Правильные цвета всех окон на экране обеспечивает программа управления окнами (терминал).
В наличии могут быть не все элементы, составляющие окно. Каждый элемент, относящийся к окну занимает одну позицию экрана, как лидирующий или завершающий атрибут, или две позиции экрана, как рамка или атрибуты рамки. В случае наличия всех элементов окна его ширина не будет превышать ширины экрана минус 6 позиций, а высота – высоты экрана минус 2 позиции.
Пример структуры окна без рамки и ее атрибутов
Окно без атрибутов
Терминал 5250 имеет блочно-ориентированную организацию потока данных, а атрибуты на экране располагаются отдельно от символов информации. К тому же терминал сам исполняет функцию менеджера окон, отслеживает их взаимное расположение, перемещение и обеспечивает правильное отображение элементов окон в соответствии с их иерархией.
DSM в явном виде поддерживает все необходимые операции с экраном, хотя и довольно специфичен и непривычен в применении, что объясняется особенностями потока данных 5250. Первоначально была предпринята попытка максимально использовать возможности DSM в плане, касающемся оконных конструкций. Но это оказалось не лучшим вариантом, поскольку система управления окнами, восстановления содержимого окон и экрана была не самой удобной и не обеспечивала необходимых требований к визуальному восприятию, иногда передаче подлежал излишне большой объем информации, что в конце концов сказывалось на удобстве использования и быстроте программы. В то же время попытка реализации экранных окон своими силами показала неплохие результаты в плане быстродействия и удобства работы. В этом направлении и было принято решение двигаться далее.
Безусловно, базовые вопросы взаимодействия с терминалом 5250 никуда не делись и лежат в основе нашей разработки. Основными особенностями работы при этом является блочно-ориентированное взаимодействие с терминалом и наличие высоких функциональных возможностей последнего. Можно сказать, что терминал выполняет роль полноценного менеджера экрана, а DSM – обертки над протоколом обмена данными, скрывающей детали его реализации
Так получилось, что к моменту исследования у меня был достаточно богатый положительный опыт использования библиотеки CURSES. Этот опыт был для проекта Enterprise-уровня. Я взял на себя смелость посмотреть, насколько прежние мысли ложатся на требования, возможности и ограничения, предъявляемые со стороны DSM.
Кроме того, библиотека CURSES в UNIX-системах фактически является стандартом текстового ввода/вывода, и было принято решение попытаться использовать имеющийся опыт для написания функционала ввода/вывода для терминала 5250.
Снова погрузимся в теорию и терминологию. А куда без нее?
Элементы, составляющие окно в нашей системе, не отличаются от аналогичных в терминале. При этом возможно создание следующих экранных конструкций:
И если окна (WIN, SUBWIN) проецируются в содержащую их структуру целиком, то проекция PADа зависит от соотношения его размеров и внешней структуры. В случае, например, когда PAD больше экрана, на экран проецируется только часть PADа.
На базе структур созданы предопределенные конструкции:
Определены следующие операции с окнами:
Ввод данных обеспечивается терминалом. Это связано с тем, что эта функциональность реализована терминалом, и подавляющая часть клавиш недоступна для иного использования, кроме как для ввода информации в поля ввода терминала. Информация, введенная в поля ввода, становится доступной только по завершении редактирования. Таким образом идеология вывода информации в значительной степени базируется на идеях библиотеки CURSES, в то время как ввод обеспечивается исключительно терминалом. Эта ситуация порождает некоторые особенности синхронизации данных между прикладной программой и терминалом, к тому же переносит акцент в организации управления экраном и вывода данных в прикладную задачу.
Поля ввода на экране терминала создаются с помощью API DSM и инициализируются значениями с виртуального экрана.
Данные, введенные в поля ввода с клавиатуры, не отображаются ни в одном окне, а, следовательно, и в виртуальном экране программы. Поэтому необходимо прочитать введенную информацию, учесть ее изменения в программе, модифицировать переменные и отобразить ее в виртуальном экране программы, чтобы его содержимое совпало с экраном терминала.
Фигуры с выделенным красным цветом контуром отображают синхронизированные состояния терминала и виртуального экрана.
Для учета иерархии окон и более удобного управления ими была создана структура FRAME.
В своем составе FRAME имеет окна WIN и SUBWIN и, возможно, PAD. Окна разделяют одну и ту же область памяти, но SUBWIN меньше на величину рамки и сопутствующих элементов. Фактически это изолированная область вывода. Фреймы же поддерживают иерархию окон и позволяют обновлять их в нужном порядке, что обеспечивает целостность результирующего экрана. Кроме того, осуществляется надзор за атрибутами всей выводимой на экран информации с учетом иерархии окон. Каждый фрейм характеризуется наличием или отсутствием рамки, ее формой, строки заголовка и сноски, атрибутами (цветами элементов). В дополнение к операциям с окнами, фрейм позволяет работать с иерархией окон, перемещать окна в ней, делать их невидимыми и наоборот.
Вывод информации в окна осуществляется с некоторой оптимизацией, с отметкой измененных символов, чтобы в дальнейшем уменьшить объем передаваемых данных.
Вывод информации в эти оконные конструкции не приводит к изменению состояния экрана до вызова команды синхронизации содержимого окна и виртуального экрана и команды обновления физического экрана терминала. Фактически работа строится на предположении, что виртуальный и физический экраны соответствуют друг другу, и на мерах по обеспечению этого.
Поля ввода, управляемые терминалом, несколько выбиваются из этого порядка. Во-первых, поля ввода способны принимать данные не только с клавиатуры, но и с экрана. Это позволяет инициализировать их нужными значениями и изменять их содержимое при необходимости. Во-вторых, необходимо обеспечить синхронизацию полей ввода с соответствующими оконными структурами. Иначе возникает достаточно труднонаходимая неоднозначность. В связи с тем, что об окнах ASDU терминалу ничего не известно, а поля ввода создаются по определенным правилам и с заданными ограничениями (в частности, слева направо и сверху вниз, без возможности наложения), вопрос создания, удаления и изменения полей решается динамически при соответствующих изменениях окон. Так, при создании нескольких окон с полями ввода поля удаляются из нижележащего окна и создаются в окне, лежащем выше по стеку. Фактически в каждый момент времени существуют только те поля, которые относятся к последнему окну в иерархии. Поля, относящиеся к одному окну, объединяются в FIELD_SET, что позволяет в том числе и управлять ими одновременно и однообразно.
Процесс вывода информации в окна и на виртуальные экраны подвергается оптимизации на каждом этапе. Поэтому, несмотря на возможно значительный объем выводимых данных, в итоге между прикладной программой и терминалом пересылаются довольно небольшие объемы, а фактически – только изменения экрана. Это подтвердила и трассировка обмена программы и терминала. К тому же, везде, где только можно, используются операции буферизации потока команд DSM. В частности, практически все команды от одного обновления экрана до другого пересылаются в одном буфере. Наблюдения показали, что это значительно увеличивает скорость обмена и реакции терминала.
Функции управления окнами и их содержимым довольно просты и интуитивно понятны. По своему смыслу и синтаксису они близки к соответствующим функциям библиотеки CURSES в UNIX, за исключением функций ввода данных. Вот некоторые из функций работы с экраном:
Разработанных базовых функций вполне достаточно для осуществления ввода информации с терминала и вывода информации на экран. Для удобства работы на их основе были реализованы более сложные конструкции, такие как окна вывода сообщений, разнообразные меню, формы ввода данных, табличный просмотр результатов выполнения SQL-запросов, подключена ли мышь.
Теперь посмотрим, что же можно сделать с практической точки зрения. Будет также интересно сравнить «как было» и «как стало». Надеюсь, что следующий раздел будет интересен не только пытливому и цепкому взгляду опытного и погруженного в тему человеку.
Теория закончилась. Давайте посмотрим на примеры реализации. Посмотрим, стало ли веселее и на правильном ли мы пути. Приведу несколько примеров использования библиотеки работы с экраном.
Вот так выглядит программа, выводящая на стандартный экран строки разного цвета, и результат ее работы:
Под спойлером приведена программа, написанная на С++ с использованием ASDU, функционально аналогичная приведенной выше на языке RPG с использованием DDS.
int main(int argc, char **argv)
{
char *msg = "Ошибка вызова";
// Описание рамки окна
_Box_T box = { TRUE, '.', '.', '.', ':', ':', '.', ':', ':', ':', '.', '+' };
if(argc > 1) {
if(*argv[1] == 'A') {
msg = "Внештатная ситуация №1";
} else if(*argv[1] == 'B') {
msg = "Внештатная ситуация №2";
}
}
_Screen_T screen('3'); // Инициализация экрана. Режим 24x80
// Вывод сообщения на стандартный экран
STDSCR->mvwcenter(0, "%cПример программы, использующей%cASDU", QSN_SA_WHT, QSN_SA_RED);
_FrameDesc_T desc(&box); // Создание описания фрейма по умолчанию
desc.set_leading_attr(QSN_SA_WHT); // Установить лидирующий атрибут окна
desc.set_cur_border_attr(QSN_SA_BLU); // Установить цвет рамки текущего окна
_Frame_T frame(6, 9, 9, 63, &desc); // Создать окно
_Win_T *subwin = frame.sub(); // Получить указатель на область вывода
frame.set_color(0, QSN_SA_BLU); // Установить цвет строки 0
subwin->mvwprintw(0, 0, "WINDEMO 100"); // Вывести наименование программы
subwin->mvwprintw(0, subwin->getmaxx()-12, "%c18 ЯНВ 2019", QSN_SA_NORM);
subwin->mvwcenter(2, "%s", msg); // Вывести сообщение по центру строки 2
frame.set_color(subwin->getmaxy()-2, QSN_SA_BLU); // Установить цвет строки
subwin->mvwprintw(subwin->getmaxy()-2, 0, "F3=Выход"); // Вывести сообщение
for(;;) {
subwin->wmove(0, 0); // Установить курсор в координаты (0, 0) области вывода окна
frame.refresh(); // Обновить экран при необходимости
switch(subwin->wgetch()) { // Получить код нажатой клавиши
case QSN_F3: // F3 - выход
break;
case QSN_ENTER: // ENTER - стереть сообщение об ошибке в последней строке
subwin->wmove(subwin->getmaxy()-1, 0);
subwin->wclrtoeol();
continue;
default: // Вывести сообщение об ошибке в последней строке
subwin->mvwprintw(subwin->getmaxy()-1, 0, "Функциональная клавиша недопустима.");
continue;
}
break;
}
// Завершение программы
return 0;
}
Если сравнить со статическим позиционным монстром, листинг которого я приводил в начале статьи, то здесь уже что-то управляемое и «живое». Конечно же, это все требует дальнейшей обертки. Важно отметить, что обертка здесь уже имеет право на существование, чего в статике не сделаешь. Таким образом, ASDU-собрат неплохо начинает себя показывать.
Реализацию этой и следующей задачи для статики можно записать в «unreal». Наверное, если пойти на принцип, то можно исхитриться, однако, стандарт явно не для таких задач.
Просмотр массива строк с возможностью поиска и выделения цветом найденных фрагментов.
Область отображения и вертикальной прокрутки может быть меньше размеров окна, что позволяет выводить дополнительную информацию. На слайде это текст TITLE и FOOTER. Возможна горизонтальная прокрутка, если ширина строк превышает ширину окна.
Просмотр массива строк с выделением текущей строки, вертикальной, горизонтальной прокруткой и возможностью организации меню.
Если предыдущие два примера простейшие и вряд ли из-за них, как говорится, стоило огород городить, то теперь мы с вами заденем конкретику, которой не было в стандартном подходе в принципе. С ASDU в «зеленом экране», например, появляется возможность вывода результата произвольного SQL-запроса на экран. А если можем вывести и подсветить отдельные строки, то можно прикрутить и редактирование, удаление, маркировку, удобную фильтрацию и прочее.
Напомню, ранее это было принципиально невозможно. Сейчас же на базе ASDU логично сделать SQL-процессор, которого нет по умолчанию. Мои коллеги сделали такой SQL-процессор и, возможно, это будет темой для одной из статей.
Давайте посмотрим на экраны и описания функциональности в том виде, как у меня получилось передать словами и скриншотами.
Реализация просмотра результатов выполнения произвольного SQL-запроса:
Каждая запись секции данных занимает 3 строки экрана. Для включения режима редактирования необходимо указание уникальных ключевых полей.
Редактироваться могут поля текущей строки которые не подвержены горизонтальной прокрутке и полностью помещаются в ячейке. Измененные значения полей сохраняются в памяти до момента их использования. Данные в ячейках могут быть приведены к виду, необходимому для редактирования, с помощью callback-функций и провалидированы. Допустимо и обратное преобразование – от редактирования к просмотру. Для данных, занимающих более одной строки, создаются композитные поля.
Есть возможность фильтрации записей и их поиска с помощью указания метасимволов или по точному совпадению. При этом фильтрация осуществляется по первоначальной информации, а поиск – по вновь введенной.
Возможно и редактирование информации в нескольких секциях одновременно.
Этот режим аналогичен предыдущему, за исключением того, что возможно редактирование выбранных полей всех строк экрана. Возможно, в некоторых простых ситуациях такое табличное редактирование полей может быть уместно.
Да-да, эти примеры работают. Повторюсь, что они не придут на смену современным фреймворкам, например, по созданию web-приложений. Более того, кто-то из читателей моего труда по-прежнему останется при мнении о «потерянном зря времени» и предложит простые понятные (и, возможно, не совсем верные =)) пути решения подобного рода задач. Буду рад и благодарен прочесть аргументированные конструктивные комментарии.
При этом стоит отметить, что эти примеры активно и успешно заменяют ранее принятые в банке подходы к реализации приложений на «зеленом экране», войдя в число наших стандартов и в состав программного обеспечения (наряду с тем, про что писал мой коллега в прошлом году aka AS/400 [3]), получившего Роспатент. Также мне приятно осознавать, что результаты муторных исследований и кропотливого труда используются не только в Альфа-Банке.
Идем дальше. Впереди осталось положенное подведение итогов, литература и прочие стандартные атрибуты повествования.
В планах более полное использование возможностей потока данных 5250:
У прочитавшего статью все еще может возникнуть справедливый вопрос «И что?». Очень хочется понимать практический эффект от такого рода исследований.
Во-первых, возможность использования приведенных здесь наработок нашла живой отклик в сердцах бизнес-пользователей. Конечно, не будем преувеличивать и ожидать, что пользователи смогли оценить именно инженерную составляющую. Они оценили удобство работы, которое было предложено взамен стандартных возможностей «от производителя», а также скорость вносимых изменений. А Time-2-Market это наше, если не все, то многое.
Во-вторых, мы уже не первый год видим пользу от системного подхода к работе. В этом контексте это про работу коллектива разработчиков. Коллектив занимается не только кодированием, но и вкладывается в подробное фокусное изучение смежных областей. Это дает свои плоды иногда даже там, где изначально не ожидали. Однако, не зря говорят, что «везет тому, кто везет». Например, проделанная работа предоставила расширенные возможности по использованию роботов в автотестировании приложений, а это уже не только инженерная история, но и прямая и заметная экономия на затратах на тестирование приложений. Но это уже тема отдельной статьи.
И, наконец, повторюсь, что очень важно в современном мире: получилось не просто решить исследовательско-инженерную задачу, но и получить довольных бизнес-пользователей. А это значит, что продолжение следует.
Спасибо, что дочитали до конца.
Всем интересных задач и успехов в их разрешении.
P.S. В декабре 2019 года состоялась очередная Конференция разработчиков IBMi [9], где в том числе был сделан доклад на тему этой статьи.
Конференция разработчиков IBMi – это первая и единственная в России площадка для общения разработчиков на одноименной платформе. Здесь можно расширить круг своего профессионального общения, принести на всеобщее внимание свой кейс и пообщаться с коллегами. В этом году Конференция состоится 2-4 декабря. Читайте наши статьи, оставляйте комментарии.
Автор: lebedvlad
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/testirovanie/353581
Ссылки в тексте:
[1] IBM i.: https://ru.wikipedia.org/wiki/IBM_i
[2] Абелевскую премию: https://ru.wikipedia.org/wiki/%D0%90%D0%B1%D0%B5%D0%BB%D0%B5%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D0%BF%D1%80%D0%B5%D0%BC%D0%B8%D1%8F
[3] aka AS/400: https://habr.com/ru/company/alfa/blog/446332
[4] Image: https://habr.com/ru/company/alfa/blog/504300/
[5] PASE: https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_73/rzalf/rzalfintro.htm
[6] OPM: https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/ilec/history.htm
[7] ILE: https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/ilec/introc1.htm
[8] при всех преимуществах новой ILE-концепции: https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_73/ilec/bcbas.htm
[9] Конференция разработчиков IBMi: https://www.youtube.com/watch?v=K83bmneWwJk&feature=emb_logo
[10] Источник: https://habr.com/ru/post/504300/?utm_source=habrahabr&utm_medium=rss&utm_campaign=504300
Нажмите здесь для печати.